Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
19ca4796be
|
@ -42,6 +42,7 @@ tmtags
|
|||
|
||||
*.py[co]
|
||||
.coverage
|
||||
*.o
|
||||
|
||||
# ==============================================================================
|
||||
# Logs and dumps
|
||||
|
@ -49,6 +50,7 @@ tmtags
|
|||
|
||||
npm-debug.log
|
||||
private/
|
||||
*.trace
|
||||
|
||||
# ==============================================================================
|
||||
# Build system output
|
||||
|
@ -70,3 +72,16 @@ build-out/
|
|||
build-gen/
|
||||
build-bin/
|
||||
build-test/
|
||||
|
||||
# ==============================================================================
|
||||
# Local-only paths
|
||||
# ==============================================================================
|
||||
|
||||
.vagrant
|
||||
attic/
|
||||
third_party/binutils/binutils-2.24.tar.gz
|
||||
third_party/binutils/bin/
|
||||
third_party/binutils/powerpc-none-elf/
|
||||
third_party/binutils/share/
|
||||
third_party/binutils/binutils*
|
||||
third_party/vasm/
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
[submodule "third_party/ninja"]
|
||||
path = third_party/ninja
|
||||
url = https://github.com/martine/ninja.git
|
||||
[submodule "third_party/gyp"]
|
||||
path = third_party/gyp
|
||||
url = https://github.com/benvanik/gyp.git
|
||||
[submodule "third_party/gflags"]
|
||||
path = third_party/gflags
|
||||
url = https://github.com/benvanik/gflags.git
|
||||
|
@ -13,12 +7,15 @@
|
|||
[submodule "third_party/beaengine"]
|
||||
path = third_party/beaengine
|
||||
url = https://github.com/benvanik/beaengine.git
|
||||
[submodule "third_party/wslay"]
|
||||
path = third_party/wslay
|
||||
url = https://github.com/benvanik/wslay.git
|
||||
[submodule "third_party/jansson"]
|
||||
path = third_party/jansson
|
||||
url = https://github.com/akheron/jansson.git
|
||||
[submodule "third_party/xbyak"]
|
||||
path = third_party/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "third_party/gyp"]
|
||||
path = third_party/gyp
|
||||
url = https://chromium.googlesource.com/external/gyp
|
||||
[submodule "third_party/ninja"]
|
||||
path = third_party/ninja
|
||||
url = https://chromium.googlesource.com/external/martine/ninja
|
||||
[submodule "third_party/catch"]
|
||||
path = third_party/catch
|
||||
url = https://github.com/philsquared/Catch.git
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Make Travis use docker (for faster builds, in theory)
|
||||
sudo: false
|
||||
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
# - gcc don't really care
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx # beta only
|
||||
|
||||
# Run setup to build ninja/gyp/etc and actually build xenia.
|
||||
before_script:
|
||||
- travis_retry ./xenia-build.py setup
|
||||
- ./xenia-build.py build --debug alloy-test
|
||||
- ./xenia-build.py build --debug alloy-ppc-test
|
||||
|
||||
# Run test suite.
|
||||
script:
|
||||
- ./build/xenia/Debug/alloy-test
|
||||
- ./build/xenia/Debug/alloy-ppc-test --runtime_backend=ivm
|
||||
- ./build/xenia/Debug/alloy-ppc-test --runtime_backend=x64
|
27
README.md
27
README.md
|
@ -20,7 +20,7 @@ that.
|
|||
|
||||
## Status
|
||||
|
||||
* Some code runs. [Insert any game name here] doesn't.
|
||||
* Some code runs. [Insert any game name here] doesn't. Except Frogger 2. Yep.
|
||||
* Asserts! Crashes! Hangs! Blank screens!
|
||||
|
||||
## Disclaimer
|
||||
|
@ -35,7 +35,7 @@ legally purchased devices and games and information made public on the internet
|
|||
|
||||
Windows 8.1+:
|
||||
|
||||
# install python 2.7 and VS2013
|
||||
# install python 2.7 and VS2013/2015
|
||||
git clone https://github.com/benvanik/xenia.git
|
||||
cd xenia
|
||||
xb setup
|
||||
|
@ -47,7 +47,8 @@ update gyp files/etc.
|
|||
## Building
|
||||
|
||||
See [building](docs/building.md) for setup and information about the
|
||||
`xenia-build` script.
|
||||
`xenia-build` script. When writing code, check the [style guide](docs/style_guide.md)
|
||||
and be sure to clang-format!
|
||||
|
||||
## Contributors Wanted!
|
||||
|
||||
|
@ -55,19 +56,11 @@ Have some spare time, know advanced C++, and want to write an emulator?
|
|||
Contribute! There's a ton of work that needs to be done, a lot of which
|
||||
is wide open greenfield fun.
|
||||
|
||||
That said, the project is currently undergoing a lot of major foundational
|
||||
development and core pieces are changing rapidly and poorly documented.
|
||||
It'll be difficult to casually hack things in unless you know what you're
|
||||
doing.
|
||||
|
||||
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)
|
||||
* Write an [OpenGL driver](https://github.com/benvanik/xenia/issues/59)
|
||||
* Add input drivers for [OSX](https://github.com/benvanik/xenia/issues/61) and [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
|
||||
* Start [hacking on audio](https://github.com/benvanik/xenia/issues/62)
|
||||
* Build a [virtual LIVE service](https://github.com/benvanik/xenia/issues/64)
|
||||
* Add input drivers for [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
|
||||
|
||||
See more projects [good for contributors](https://github.com/benvanik/xenia/issues?labels=good+for+contributors&page=1&state=open). It's a good idea to ask on IRC/the bugs before beginning work
|
||||
on something.
|
||||
|
@ -91,15 +84,13 @@ enough to run games at a decent speed the answer is no.
|
|||
### What about Linux/OSX?
|
||||
|
||||
The project is designed to support non-Windows platforms but until it's running
|
||||
games it's not worth the maintenance burden. If you're a really passionate
|
||||
Linux/OSX-based developer and want to help out, run Bootcamp/VM and contribute
|
||||
an OpenGL 4 driver - that'll be the most difficult part in porting to
|
||||
non-Windows platforms.
|
||||
games it's not worth the maintenance burden. OSX will likely remain unsupported
|
||||
until Apple supports OpenGL 4.3 or higher.
|
||||
|
||||
### What kind of GPU do I need?
|
||||
|
||||
DirectX 11 support is required. To get full speed and compatibility Mantle may
|
||||
be required in the future.
|
||||
OpenGL 4.5 support and drivers are required. To get full speed and compatibility
|
||||
GL-next may eventually be required.
|
||||
|
||||
### Have you heard of LLVM/asmjit/jitasm/luajit/etc?
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
$script = <<SCRIPT
|
||||
set -e
|
||||
echo "Prepping system..."
|
||||
sudo apt-get update > /dev/null
|
||||
sudo apt-get -y install git
|
||||
|
||||
echo "Building binutils..."
|
||||
sudo apt-get -y install git build-essential texinfo flex bison
|
||||
cd /vagrant/third_party/binutils/
|
||||
./build.sh
|
||||
|
||||
echo "...and it's done!"
|
||||
SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "hashicorp/precise64"
|
||||
|
||||
config.vm.provision "shell", inline: $script
|
||||
|
||||
# If true, then any SSH connections made will enable agent forwarding.
|
||||
# Default value: false
|
||||
# config.ssh.forward_agent = true
|
||||
|
||||
# Provider-specific configuration so you can fine-tune various
|
||||
# backing providers for Vagrant. These expose provider-specific options.
|
||||
# Example for VirtualBox:
|
||||
#
|
||||
# config.vm.provider "virtualbox" do |vb|
|
||||
# # Don't boot with headless mode
|
||||
# vb.gui = true
|
||||
#
|
||||
# # Use VBoxManage to customize the VM. For example to change memory:
|
||||
# vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
# end
|
||||
#
|
||||
# View the documentation for the provider you're using for more
|
||||
# information on available options.
|
||||
config.vm.provider "hyperv" do |hv, override|
|
||||
override.vm.synced_folder ".", "/vagrant", type: "smb"
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC --abort_before_entry=true "$@"
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC "$@"
|
||||
|
||||
|
||||
# TODO(benvanik): add --valgrind and --leaks
|
||||
# xbb --debug && rm valgrind.txt && valgrind --log-file=valgrind.txt --dsymutil=yes build/xenia/debug/xenia-run "$@"
|
||||
# --track-origins=yes --leak-check=full
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
CONFIG=release
|
||||
case "$*" in
|
||||
(*--debug*) CONFIG=debug;;
|
||||
esac
|
||||
|
||||
EXEC=$DIR/../build/xenia/$CONFIG/xenia-test
|
||||
|
||||
if [ ! -f "$EXEC" ]; then
|
||||
python $DIR/../xenia-build.py build --$CONFIG
|
||||
fi
|
||||
|
||||
$EXEC "$@"
|
|
@ -1,468 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.left-align {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
min-width: 900px;
|
||||
min-height: 350px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.app-header {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
align-self: auto;
|
||||
}
|
||||
.app-header .navbar {
|
||||
border-radius: 0;
|
||||
min-height: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.app-body {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
align-self: auto;
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.disconnected .app-body {
|
||||
opacity: 0.25;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tab-pane {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.app-console {
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
align-self: auto;
|
||||
border-top-width: 2px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
.console {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
padding: 5px;
|
||||
}
|
||||
.console-part-log {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
align-self: auto;
|
||||
}
|
||||
.console-log-outer {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 1px solid #ddd;
|
||||
overflow-y: scroll;
|
||||
height: 100px;
|
||||
}
|
||||
.console-part-input {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
align-self: auto;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.console-input-left {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
.console-input-middle {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.console-input-right {
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.debugger-main {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.debugger-header {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
align-self: auto;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 5px;
|
||||
}
|
||||
.debugger-body {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
align-self: auto;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.debugger-fnlist {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-width: 270px;
|
||||
}
|
||||
.debugger-fnlist-header {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.debugger-fnlist-header-left {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.debugger-fnlist-header-right {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.debugger-fnlist-body {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
padding: 5px;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
position: relative;
|
||||
}
|
||||
.debugger-fnlist-list {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 5px;
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.debugger-fnlist-list > table > tbody > tr > td {
|
||||
padding: 0;
|
||||
line-height: 1.2em;
|
||||
font-family: monospace;
|
||||
border: 0;
|
||||
}
|
||||
.debugger-fnlist-footer {
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
}
|
||||
.debugger-fnlist-footer .input-group {
|
||||
width: 100%;
|
||||
}
|
||||
.debugger-fnview-outer {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
border-left: 2px solid #ddd;
|
||||
border-right: 2px solid #ddd;
|
||||
min-width: 720px;
|
||||
}
|
||||
.debugger-fnview {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.debugger-fnview-header {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.debugger-fnview-header-left {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
padding: 5px;
|
||||
line-height: 1;
|
||||
}
|
||||
.debugger-fnview-header-name {
|
||||
font-size: 21px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.debugger-fnview-header-address {
|
||||
font-family: monospace;
|
||||
}
|
||||
.debugger-fnview-header-right {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.debugger-fnview-body {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.debugger-fnview-codeview {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
}
|
||||
.debugger-fnview-codeview .CodeMirror {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.debugger-fnview-gutter-icon {
|
||||
width: 30px;
|
||||
}
|
||||
.debugger-fnview-gutter-icon-el {
|
||||
padding-left: 7px;
|
||||
position: relative;
|
||||
top: -6px;
|
||||
display: inline-block;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
font-size: 28px;
|
||||
}
|
||||
.debugger-fnview-gutter-addr {
|
||||
width: 70px;
|
||||
}
|
||||
.debugger-fnview-gutter-addr-el {
|
||||
}
|
||||
.debugger-fnview-gutter-addr-el-inactive {
|
||||
color: #999;
|
||||
}
|
||||
.debugger-fnview-gutter-code {
|
||||
width: 76px;
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
.debugger-fnview-gutter-code-el {
|
||||
padding-left: 6px;
|
||||
color: #aaa;
|
||||
}
|
||||
.debugger-fnview-line-highlight-bg {
|
||||
background-color: red;
|
||||
}
|
||||
.debugger-fnview-graphview {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
border-left: 1px solid #ddd;
|
||||
min-width: 100px;
|
||||
}
|
||||
.debugger-fnview-footer {
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
}
|
||||
.debugger-tools {
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 40%;
|
||||
}
|
||||
.debugger-tools-threads {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
}
|
||||
.debugger-tools-threads {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.debugger-tools-threads-header-left {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.debugger-tools-threads-header-right {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.debugger-tools-callstack {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
padding: 5px;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
position: relative;
|
||||
height: 100px;
|
||||
}
|
||||
.debugger-tools-registers {
|
||||
order: 3;
|
||||
flex: 1 1 auto;
|
||||
padding: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.debugger-tools-registers-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.debugger-tools-registers-entry {
|
||||
flex: 0 0 auto;
|
||||
font-family: monospace;
|
||||
padding-right: 5px;
|
||||
width: 160px;
|
||||
}
|
||||
.debugger-tools-registers-entry .name {
|
||||
font-weight: bold;
|
||||
width: 26px;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
.debugger-tools-registers-entry .value {
|
||||
}
|
||||
.debugger-tools-registers-container.special {
|
||||
height: 46px;
|
||||
}
|
||||
.debugger-tools-registers-container.gpr {
|
||||
height: 332px;
|
||||
}
|
||||
.special .debugger-tools-registers-entry,
|
||||
.gpr .debugger-tools-registers-entry {
|
||||
width: 300px;
|
||||
}
|
||||
.debugger-tools-registers-entry .hex-value {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
width: 130px;
|
||||
text-align: right;
|
||||
}
|
||||
.debugger-tools-registers-entry .int-value {
|
||||
display: inline-block;
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
width: 92px;
|
||||
text-align: right;
|
||||
}
|
||||
.debugger-tools-registers-container.fpr {
|
||||
height: 172px;
|
||||
}
|
||||
.fpr .debugger-tools-registers-entry {
|
||||
width: 170px;
|
||||
}
|
||||
.fpr .debugger-tools-registers-entry .value {
|
||||
white-space: pre;
|
||||
}
|
||||
.vec .debugger-tools-registers-entry {
|
||||
width: 160px;
|
||||
}
|
||||
.vec .debugger-tools-registers-entry .name {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
.debugger-module-info {
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
}
|
||||
.debugger-module-info .modal-dialog {
|
||||
width: 60vw;
|
||||
}
|
||||
.debugger-module-info.fade .modal-dialog {
|
||||
-webkit-transition: none;
|
||||
-webkit-transform: translate(0,0);
|
||||
}
|
||||
.debugger-module-info div {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.debugger-module-info .modal-body {
|
||||
max-width: 60vw;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.debugger-module-info-outer-table {
|
||||
}
|
||||
.debugger-module-info-outer-table tbody tr td:nth-child(1) {
|
||||
width: 110px;
|
||||
}
|
||||
.debugger-module-info-inner-table {
|
||||
width: 300px;
|
||||
}
|
||||
.debugger-module-info-inner-table td:nth-child(1) {
|
||||
text-align: right;
|
||||
}
|
||||
.debugger-module-info-headers {
|
||||
width: 400px;
|
||||
}
|
||||
.debugger-module-info-sections {
|
||||
width: 400px;
|
||||
}
|
||||
.debugger-module-info-static-libraries {
|
||||
width: 400px;
|
||||
}
|
||||
.debugger-module-info-imports {
|
||||
width: 900px;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(1) {
|
||||
width: 40px;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(2) {
|
||||
width: 64px;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(3) {
|
||||
width: 60px;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(4) {
|
||||
width: 100%;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(5) {
|
||||
width: 80px;
|
||||
}
|
||||
.debugger-module-info-imports td:nth-child(6) {
|
||||
width: 80px;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
TODO: APU
|
|
@ -1,135 +0,0 @@
|
|||
<div class="debugger-main" ng-controller="CodeTabController">
|
||||
<div class="debugger-header">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-success" ng-click="app.session.continueExecution()" ng-disabled="!app.session.paused">
|
||||
<span class="glyphicon glyphicon-play"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="app.session.breakExecution()" ng-disabled="app.session.paused">
|
||||
<span class="glyphicon glyphicon-pause"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default" ng-click="showLocation()" ng-disabled="!app.session.paused">
|
||||
<span class="glyphicon glyphicon glyphicon-arrow-right"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" ng-click="app.session.stepNext()" ng-disabled="!app.session.paused">
|
||||
<span class="glyphicon glyphicon-step-forward"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-body">
|
||||
<div class="debugger-fnlist">
|
||||
<div class="debugger-fnlist-header">
|
||||
<div class="debugger-fnlist-header-left btn-group btn-group-xs full-width">
|
||||
<button type="button" class="btn btn-default dropdown-toggle left-align full-width" data-toggle="dropdown">
|
||||
{{selectedModule.name}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="module in moduleList"><a href="" ng-click="selectModule(module)">{{module.name}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="debugger-fnlist-header-right btn-group btn-group-xs">
|
||||
<button type="button" class="btn btn-default" ng-click="showModuleInfo()">
|
||||
Info
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-fnlist-body">
|
||||
<div class="debugger-fnlist-list">
|
||||
<table class="table table-hover">
|
||||
<tr ng-repeat="fn in functionList track by $index | orderBy:'address'">
|
||||
<td><a xe-coderef="{{fn.address|hex32}}">{{fn.name}}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-fnlist-footer">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control" placeholder="Filter" ng-model="functionFilter.$" ui-escape="functionFilter = ''">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-fnview-outer" ui-view></div>
|
||||
<div class="debugger-tools">
|
||||
<div class="debugger-tools-threads">
|
||||
<div class="debugger-tools-threads-header-left btn-group btn-group-xs full-width">
|
||||
<button type="button" class="btn btn-default left-align dropdown-toggle full-width" data-toggle="dropdown">
|
||||
Thread {{app.session.activeThread.id}}: {{app.session.activeThread.name}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="thread in app.session.state.threadList | orderBy:'id'">
|
||||
<a href="" ng-click="app.session.activeThread = thread;">Thread {{thread.id}}: {{thread.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="debugger-tools-threads-header-right btn-group btn-group-xs">
|
||||
<button type="button" class="btn btn-default" ng-click="showThreadInfo()">
|
||||
Info
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-tools-callstack">
|
||||
callstack
|
||||
</div>
|
||||
<div class="debugger-tools-registers">
|
||||
<div class="debugger-tools-registers-container special">
|
||||
<div class="debugger-tools-registers-entry">
|
||||
<span class="name">pc</span>
|
||||
<input class="hex-value"
|
||||
value="{{app.session.activeThread.context.pc|hex32}}"
|
||||
popover="TODO: template to link to code/memory"
|
||||
popover-trigger="focus"
|
||||
onclick="this.select()">
|
||||
</div>
|
||||
<div class="debugger-tools-registers-entry">
|
||||
<span class="name">lr</span>
|
||||
<input class="hex-value"
|
||||
value="{{app.session.activeThread.context.lr|hex32}}"
|
||||
popover="TODO: template to link to code/memory"
|
||||
popover-trigger="focus"
|
||||
onclick="this.select()">
|
||||
</div>
|
||||
<div class="debugger-tools-registers-entry">
|
||||
<span class="name">ctr</span>
|
||||
<input class="hex-value"
|
||||
value="{{app.session.activeThread.context.ctrh}}"
|
||||
popover="TODO: template to link to code/memory"
|
||||
popover-trigger="focus"
|
||||
onclick="this.select()">
|
||||
<input class="int-value"
|
||||
value="{{app.session.activeThread.context.ctrs}}"
|
||||
onclick="this.select()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-tools-registers-container gpr">
|
||||
<div ng-repeat="v in app.session.activeThread.context.r track by $index"
|
||||
class="debugger-tools-registers-entry">
|
||||
<span class="name">r{{$index}}</span>
|
||||
<input class="hex-value"
|
||||
value="{{app.session.activeThread.context.rh[$index]}}"
|
||||
popover="TODO: template to link to code/memory"
|
||||
popover-trigger="focus"
|
||||
onclick="this.select()">
|
||||
<input class="int-value"
|
||||
value="{{app.session.activeThread.context.rs[$index]}}"
|
||||
onclick="this.select()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-tools-registers-container fpr">
|
||||
<div ng-repeat="v in app.session.activeThread.context.f track by $index"
|
||||
class="debugger-tools-registers-entry">
|
||||
<span class="name">f{{$index}}</span>
|
||||
<span class="value" tooltip="{{app.session.activeThread.context.fh[$index]}}">{{v|exp8}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-tools-registers-container">
|
||||
<div ng-repeat="v in app.session.activeThread.context.v track by $index"
|
||||
class="debugger-tools-registers-entry vec">
|
||||
<span class="name">v{{$index}}</span>
|
||||
<span class="value">{{v}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.code', [
|
||||
'ui.bootstrap',
|
||||
'xe.log',
|
||||
'xe.session'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('CodeTabController', function(
|
||||
$rootScope, $scope, $modal, app, log) {
|
||||
$scope.app = app;
|
||||
$scope.moduleList = [];
|
||||
$scope.selectedModule = null;
|
||||
$scope.functionList = [];
|
||||
|
||||
function refresh() {
|
||||
if (!app.session) {
|
||||
$scope.moduleList = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.moduleList = app.session.state.getModuleList();
|
||||
if (!$scope.selectedModule) {
|
||||
if ($scope.moduleList.length) {
|
||||
$scope.selectModule($scope.moduleList[0]);
|
||||
}
|
||||
} else {
|
||||
$scope.selectModule($scope.selectedModule);
|
||||
}
|
||||
|
||||
console.log('refresh');
|
||||
};
|
||||
$rootScope.$on('refresh', refresh);
|
||||
|
||||
$scope.selectModule = function(module) {
|
||||
var moduleChange = module != $scope.selectedModule;
|
||||
$scope.selectedModule = module;
|
||||
|
||||
if (moduleChange) {
|
||||
$scope.functionList = [];
|
||||
}
|
||||
|
||||
$scope.functionList = app.session.state.getFunctionList(module.name);
|
||||
};
|
||||
|
||||
$scope.showModuleInfo = function() {
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'assets/ui/code/module-info.html',
|
||||
controller: 'ModuleInfoController',
|
||||
windowClass: 'debugger-module-info',
|
||||
resolve: {
|
||||
moduleName: function() {
|
||||
return $scope.selectedModule.name;
|
||||
},
|
||||
moduleInfo: function() {
|
||||
return app.session.state.getModule(
|
||||
$scope.selectedModule.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showThreadInfo = function() {
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'assets/ui/code/thread-info.html',
|
||||
controller: 'ThreadInfoController',
|
||||
windowClass: 'debugger-module-info',
|
||||
resolve: {
|
||||
thread: function() {
|
||||
return app.session.activeThread;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showLocation = function() {
|
||||
//
|
||||
};
|
||||
|
||||
if (app.session.dataSource) {
|
||||
refresh();
|
||||
}
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
<div class="debugger-fnview" ng-controller="FunctionViewController">
|
||||
<div class="debugger-fnview-header">
|
||||
<div class="debugger-fnview-header-left">
|
||||
<span class="debugger-fnview-header-name" ng-bind="fn.name"></span><br/>
|
||||
<span class="debugger-fnview-header-address">(0x{{fn.startAddress | hex32}}-0x{{fn.endAddress | hex32}})</span>
|
||||
</div>
|
||||
<div class="debugger-fnview-header-right">
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'source'">PPC</button>
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'rawHir'">HIR (raw)</button>
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'hir'">HIR</button>
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'rawLir'">LIR (raw)</button>
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'lir'">LIR</button>
|
||||
<button type="button" class="btn btn-default" ng-model="codeType" btn-radio="'machineCode'">MC</button>
|
||||
</div>
|
||||
<!--
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default">1</button>
|
||||
<button type="button" class="btn btn-default">2</button>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
Dropdown
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Dropdown link</a></li>
|
||||
<li><a href="#">Dropdown link</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-fnview-body">
|
||||
<div class="debugger-fnview-codeview">
|
||||
<textarea class="debugger-fnview-textarea"></textarea>
|
||||
</div>
|
||||
<div class="debugger-fnview-graphview">
|
||||
graph!
|
||||
</div>
|
||||
</div>
|
||||
<div class="debugger-fnview-footer">
|
||||
footer
|
||||
</div>
|
||||
</div>
|
|
@ -1,268 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.code.functionView', [
|
||||
'xe.log',
|
||||
'xe.session'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('FunctionViewController', function(
|
||||
$rootScope, $scope, $location, app, log, Breakpoint) {
|
||||
$scope.codeType = 'source';
|
||||
$scope.highlightInfo = null;
|
||||
|
||||
function refresh() {
|
||||
if (!app.session) {
|
||||
$scope.fn = null;
|
||||
return;
|
||||
}
|
||||
app.session.state.fetchFunction($scope.functionAddress).then(function(fn) {
|
||||
$scope.fn = fn;
|
||||
updateCode();
|
||||
}, function(e) {
|
||||
log.error('Unable to fetch function.');
|
||||
});
|
||||
};
|
||||
$rootScope.$on('refresh', refresh);
|
||||
$scope.$watch('functionAddress', refresh);
|
||||
|
||||
function updateHighlight(address) {
|
||||
if (!$scope.sourceLines || $scope.codeType != 'source') {
|
||||
return;
|
||||
}
|
||||
if ($scope.highlightInfo) {
|
||||
if ($scope.highlightInfo.address == address) {
|
||||
return;
|
||||
}
|
||||
var oldLine = $scope.highlightInfo.line;
|
||||
if ($scope.highlightInfo.widget) {
|
||||
$scope.highlightInfo.widget.clear();
|
||||
}
|
||||
$scope.highlightInfo = null;
|
||||
updateLine(oldLine);
|
||||
}
|
||||
// TODO(benvanik): a better mapping.
|
||||
var line = -1;
|
||||
for (var n = 0; n < $scope.sourceLines.length; n++) {
|
||||
var sourceLine = $scope.sourceLines[n];
|
||||
if (sourceLine[0] == 'i' &&
|
||||
sourceLine[1] == address) {
|
||||
line = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (line != -1) {
|
||||
$scope.highlightInfo = {
|
||||
address: address,
|
||||
line: line,
|
||||
widget: null
|
||||
};
|
||||
updateLine(line);
|
||||
}
|
||||
};
|
||||
$scope.$watch(function() {
|
||||
return $location.search();
|
||||
}, function(search) {
|
||||
updateHighlight(parseInt(search.a, 16));
|
||||
});
|
||||
|
||||
var textArea = document.querySelector('.debugger-fnview-textarea');
|
||||
$scope.codeMirror = CodeMirror.fromTextArea(textArea, {
|
||||
mode: 'javascript',
|
||||
theme: 'default',
|
||||
indentUnit: 2,
|
||||
tabSize: 2,
|
||||
lineNumbers: false,
|
||||
gutters: [
|
||||
'debugger-fnview-gutter-icon',
|
||||
'debugger-fnview-gutter-addr',
|
||||
'debugger-fnview-gutter-code'
|
||||
],
|
||||
readOnly: true
|
||||
});
|
||||
|
||||
function hex32(number) {
|
||||
var str = "" + number.toString(16).toUpperCase();
|
||||
while (str.length < 8) str = "0" + str;
|
||||
return str;
|
||||
};
|
||||
|
||||
function updateSourceCode(fn) {
|
||||
var cm = $scope.codeMirror;
|
||||
if (!fn) {
|
||||
$scope.sourceLines = [];
|
||||
cm.setValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
var doc = cm.getDoc();
|
||||
|
||||
var lines = fn.disasm.source.lines;
|
||||
$scope.sourceLines = lines;
|
||||
|
||||
var textContent = [];
|
||||
for (var n = 0; n < lines.length; n++) {
|
||||
var line = lines[n];
|
||||
textContent.push(line[3]);
|
||||
}
|
||||
cm.setValue(textContent.join('\n'));
|
||||
|
||||
for (var n = 0; n < lines.length; n++) {
|
||||
var line = lines[n];
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.classList.add('debugger-fnview-gutter-addr-el');
|
||||
el.innerText = hex32(line[1]);
|
||||
cm.setGutterMarker(n, 'debugger-fnview-gutter-addr', el);
|
||||
if (line[0] != 'i') {
|
||||
el.classList.add('debugger-fnview-gutter-addr-el-inactive');
|
||||
}
|
||||
|
||||
if (line[0] == 'i') {
|
||||
el = document.createElement('div');
|
||||
el.classList.add('debugger-fnview-gutter-code-el');
|
||||
el.innerText = hex32(line[2]);
|
||||
cm.setGutterMarker(n, 'debugger-fnview-gutter-code', el);
|
||||
|
||||
updateLine(n);
|
||||
}
|
||||
}
|
||||
};
|
||||
function updateCode() {
|
||||
var cm = $scope.codeMirror;
|
||||
var fn = $scope.fn || null;
|
||||
var codeType = $scope.codeType;
|
||||
|
||||
var gutters;
|
||||
switch (codeType) {
|
||||
case 'source':
|
||||
gutters = [
|
||||
'debugger-fnview-gutter-icon',
|
||||
'debugger-fnview-gutter-addr',
|
||||
'debugger-fnview-gutter-code'
|
||||
];
|
||||
break;
|
||||
default:
|
||||
gutters = [
|
||||
'debugger-fnview-gutter-icon',
|
||||
'debugger-fnview-gutter-addr'
|
||||
];
|
||||
break;
|
||||
}
|
||||
cm.setOption('gutters', gutters);
|
||||
|
||||
cm.clearGutter('debugger-fnview-gutter-icon');
|
||||
cm.clearGutter('debugger-fnview-gutter-addr');
|
||||
cm.clearGutter('debugger-fnview-gutter-code');
|
||||
|
||||
// Set last to make all option changes stick.
|
||||
switch (codeType) {
|
||||
case 'source':
|
||||
cm.operation(function() {
|
||||
updateSourceCode(fn);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
var value = fn ? fn.disasm[codeType] : null;
|
||||
cm.setValue(value || '');
|
||||
break;
|
||||
}
|
||||
};
|
||||
$scope.$watch('codeType', updateCode);
|
||||
|
||||
function updateLine(line) {
|
||||
var sourceLine = $scope.sourceLines[line];
|
||||
var cm = $scope.codeMirror;
|
||||
if (sourceLine[0] != 'i') {
|
||||
return;
|
||||
}
|
||||
var address = sourceLine[1];
|
||||
var breakpoint = app.session.breakpoints[address];
|
||||
var el;
|
||||
if (breakpoint && breakpoint.type == 'code') {
|
||||
el = document.createElement('span');
|
||||
el.classList.add('debugger-fnview-gutter-icon-el');
|
||||
if (breakpoint.enabled) {
|
||||
el.innerHTML = '●';
|
||||
} else {
|
||||
el.innerHTML = '◌';
|
||||
}
|
||||
} else {
|
||||
el = null;
|
||||
}
|
||||
cm.setGutterMarker(line, 'debugger-fnview-gutter-icon', el);
|
||||
|
||||
var highlightInfo = $scope.highlightInfo;
|
||||
if (highlightInfo && highlightInfo.line == line) {
|
||||
/*
|
||||
if (!highlightInfo.widget) {
|
||||
el = document.createElement('div');
|
||||
el.style.width = '100%';
|
||||
el.style.height = '20px';
|
||||
el.style.backgroundColor = 'red';
|
||||
el.innerHTML = 'hi!';
|
||||
highlightInfo.widget = cm.addLineWidget(line, el, {
|
||||
coverGutter: false
|
||||
});
|
||||
cm.scrollIntoView(line, 50);
|
||||
}
|
||||
*/
|
||||
cm.addLineClass(line, 'background', 'debugger-fnview-line-highlight-bg');
|
||||
} else {
|
||||
cm.removeLineClass(line, 'background');
|
||||
}
|
||||
};
|
||||
|
||||
function toggleBreakpoint(line, sourceLine, shiftKey) {
|
||||
var address = sourceLine[1];
|
||||
var breakpoint = app.session.breakpoints[address];
|
||||
if (breakpoint) {
|
||||
// Existing breakpoint - toggle or remove.
|
||||
if (shiftKey || !breakpoint.enabled) {
|
||||
app.session.toggleBreakpoint(breakpoint, !breakpoint.enabled);
|
||||
} else {
|
||||
app.session.removeBreakpoint(breakpoint);
|
||||
}
|
||||
} else {
|
||||
// New breakpoint needed.
|
||||
breakpoint = app.session.addCodeBreakpoint(
|
||||
$scope.functionAddress, address);
|
||||
}
|
||||
|
||||
updateLine(line);
|
||||
};
|
||||
|
||||
$scope.codeMirror.on('gutterClick', function(
|
||||
instance, line, gutterClass, e) {
|
||||
if (e.which == 1) {
|
||||
if (gutterClass == 'debugger-fnview-gutter-icon' ||
|
||||
gutterClass == 'debugger-fnview-gutter-addr') {
|
||||
var sourceLine = $scope.sourceLines[line];
|
||||
if (!sourceLine || sourceLine[0] != 'i') {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
toggleBreakpoint(line, sourceLine, e.shiftKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
// $scope.codeMirror.on('gutterContextMenu', function(
|
||||
// instance, line, gutterClass, e) {
|
||||
// console.log('context menu');
|
||||
// e.preventDefault();
|
||||
// });
|
||||
// $scope.codeMirror.on('contextmenu', function(
|
||||
// instance, e) {
|
||||
// console.log('context menu main');
|
||||
// e.preventDefault();
|
||||
// });
|
||||
});
|
|
@ -1,218 +0,0 @@
|
|||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">×</button>
|
||||
<h4 class="modal-title">{{ moduleName }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table debugger-module-info-outer-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Module</td>
|
||||
<td>
|
||||
<table class="table table-hover table-condensed debugger-module-info-inner-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Module Flags</td>
|
||||
<td>{{ moduleInfo.moduleFlags | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>System Flags</td>
|
||||
<td>{{ moduleInfo.systemFlags | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EXE Address</td>
|
||||
<td><a xe-memref="{{ moduleInfo.exeAddress | hex32 }}" ng-click="$close()">{{ moduleInfo.exeAddress | hex32 }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entry Point</td>
|
||||
<td><a xe-coderef="{{ moduleInfo.exeEntryPoint | hex32 }}" ng-click="$close()">{{ moduleInfo.exeEntryPoint | hex32 }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stack Size</td>
|
||||
<td>{{ moduleInfo.exeStackSize }}b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Heap Size</td>
|
||||
<td>{{ moduleInfo.exeHeapSize }}b</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Execution</td>
|
||||
<td>
|
||||
<table class="table table-hover table-condensed debugger-module-info-inner-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Media ID</td>
|
||||
<td>{{ moduleInfo.executionInfo.mediaId | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{ moduleInfo.executionInfo.version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Base Version</td>
|
||||
<td>{{ moduleInfo.executionInfo.baseVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Title ID</td>
|
||||
<td>{{ moduleInfo.executionInfo.titleId | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Platform</td>
|
||||
<td>{{ moduleInfo.executionInfo.platform | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Executable Table</td>
|
||||
<td>{{ moduleInfo.executionInfo.executableTable | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Disc</td>
|
||||
<td>{{ moduleInfo.executionInfo.discNumber }} / {{ moduleInfo.executionInfo.discCount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Save Game ID</td>
|
||||
<td>{{ moduleInfo.executionInfo.savegameId | hex32 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Loader</td>
|
||||
<td>
|
||||
<table class="table table-hover table-condensed debugger-module-info-inner-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Image Flags</td>
|
||||
<td>{{ moduleInfo.loaderInfo.imageFlags | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Game Regions</td>
|
||||
<td>{{ moduleInfo.loaderInfo.gameRegions | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Media Flags</td>
|
||||
<td>{{ moduleInfo.loaderInfo.mediaFlags | hex32 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TLS</td>
|
||||
<td>
|
||||
<table class="table table-hover table-condensed debugger-module-info-inner-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Slot Count</td>
|
||||
<td>{{ moduleInfo.tlsInfo.slotCount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Data Size</td>
|
||||
<td>{{ moduleInfo.tlsInfo.dataSize }}b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Raw Data Address</td>
|
||||
<td>{{ moduleInfo.tlsInfo.rawDataAddress | hex32 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Raw Data Size</td>
|
||||
<td>{{ moduleInfo.tlsInfo.rawDataSize }}b</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-hover table-condensed debugger-module-info-headers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="changeSort(headerSort, 'key')">Key</a></th>
|
||||
<th><a href="" ng-click="changeSort(headerSort, 'value')">Value</a></th>
|
||||
<th><a href="" ng-click="changeSort(headerSort, 'length')">Length</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="header in moduleInfo.headers | orderBy:headerSort.column:headerSort.reverse">
|
||||
<td>{{ header.key | hex32 }}</td>
|
||||
<td>{{ header.value | hex32 }}</td>
|
||||
<td>{{ header.length }}b</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Sections</h3>
|
||||
<table class="table table-hover table-condensed debugger-module-info-sections">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="changeSort(sectionSort, '$index')">#</a></th>
|
||||
<th><a href="" ng-click="changeSort(sectionSort, 'type')">Type</a></th>
|
||||
<th><a href="" ng-click="changeSort(sectionSort, 'startAddress')">Start</a></th>
|
||||
<th><a href="" ng-click="changeSort(sectionSort, 'endAddress')">End</a></th>
|
||||
<th><a href="" ng-click="changeSort(sectionSort, 'totalLength')">Length</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="section in moduleInfo.sections | orderBy:sectionSort.column:sectionSort.reverse">
|
||||
<td>{{ $index }}</td>
|
||||
<td>{{ section.type }}</td>
|
||||
<td><a xe-memref="{{section.startAddress|hex32}}" ng-click="$close()">{{ section.startAddress | hex32 }}</a></td>
|
||||
<td><a xe-memref="{{section.endAddress|hex32}}" ng-click="$close()">{{ section.endAddress | hex32 }}</a></td>
|
||||
<td>{{ section.totalLength }}b</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Static Libraries</h3>
|
||||
<table class="table table-hover table-condensed debugger-module-info-static-libraries">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="changeSort(staticLibrarySort, 'name')">Name</a></th>
|
||||
<th><a href="" ng-click="changeSort(staticLibrarySort, 'version')">Version</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="library in moduleInfo.staticLibraries | orderBy:staticLibrarySort.column:staticLibrarySort.reverse">
|
||||
<td>{{ library.name }}</td>
|
||||
<td>{{ library.version }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Imports</h3>
|
||||
<div ng-repeat="library in moduleInfo.libraryImports | orderBy:'name'">
|
||||
<h4>{{ library.name }}</h4>
|
||||
Version: {{ library.version }} / min version: {{ library.minVersion }}<br/>
|
||||
<table class="table table-hover table-condensed debugger-module-info-imports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'implemented')">Impl</a></th>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'ordinal')">Ordinal</a></th>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'type')">Type</a></th>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'name')">Name</a></th>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'valueAddress')">Value</a></th>
|
||||
<th><a href="" ng-click="changeSort(importSort, 'thunkAddress')">Thunk</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="import in library.imports | orderBy:importSort.column:importSort.reverse"
|
||||
ng-class="{danger: !import.implemented}">
|
||||
<td>{{ import.implemented }}</td>
|
||||
<td>{{ import.ordinal | hex16 }}</td>
|
||||
<td>{{ import.type }}</td>
|
||||
<td><a href="https://www.google.com/search?as_epq={{import.name}}" target="_blank">{{ import.name }}</a></td>
|
||||
<td><a xe-memref="{{import.valueAddress|hex32}}" ng-click="$close()">{{ import.valueAddress | hex32 }}</a></td>
|
||||
<td><a xe-coderef="{{import.thunkAddress|hex32}}" ng-click="$close()">{{ import.thunkAddress | hex32 }}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.code.moduleInfo', [
|
||||
'ui.bootstrap',
|
||||
'xe.log',
|
||||
'xe.session'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('ModuleInfoController', function(
|
||||
$rootScope, $scope, $modal, log, moduleName, moduleInfo) {
|
||||
$scope.moduleName = moduleName;
|
||||
$scope.moduleInfo = moduleInfo;
|
||||
|
||||
$scope.headerSort = {
|
||||
column: 'key',
|
||||
reverse: false
|
||||
};
|
||||
$scope.sectionSort = {
|
||||
column: 'startAddress',
|
||||
reverse: false
|
||||
};
|
||||
$scope.staticLibrarySort = {
|
||||
column: 'name',
|
||||
reverse: false
|
||||
};
|
||||
$scope.importSort = {
|
||||
column: 'ordinal',
|
||||
reverse: false
|
||||
};
|
||||
$scope.changeSort = function(sort, column) {
|
||||
if (sort.column == column) {
|
||||
sort.reverse = !sort.reverse;
|
||||
} else {
|
||||
sort.column = column;
|
||||
sort.reverse = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
$scope.$close(null);
|
||||
};
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">×</button>
|
||||
<h4 class="modal-title">Thread {{ thread.id }}: {{ thread.name }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table debugger-module-info-outer-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Thread</td>
|
||||
<td>
|
||||
<table class="table table-hover table-condensed debugger-module-info-inner-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Stack Address</td>
|
||||
<td><a xe-memref="{{ thread.stackAddress | hex32 }}" ng-click="$close()">{{ thread.stackAddress | hex32 }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stack Size</td>
|
||||
<td>{{ thread.stackSize }}b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State Address</td>
|
||||
<td><a xe-memref="{{ thread.threadStateAddress | hex32 }}" ng-click="$close()">{{ thread.threadStateAddress | hex32 }}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,51 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.code.threadInfo', [
|
||||
'ui.bootstrap',
|
||||
'xe.log',
|
||||
'xe.session'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('ThreadInfoController', function(
|
||||
$rootScope, $scope, $modal, log, thread) {
|
||||
$scope.thread = thread;
|
||||
|
||||
$scope.headerSort = {
|
||||
column: 'key',
|
||||
reverse: false
|
||||
};
|
||||
$scope.sectionSort = {
|
||||
column: 'startAddress',
|
||||
reverse: false
|
||||
};
|
||||
$scope.staticLibrarySort = {
|
||||
column: 'name',
|
||||
reverse: false
|
||||
};
|
||||
$scope.importSort = {
|
||||
column: 'ordinal',
|
||||
reverse: false
|
||||
};
|
||||
$scope.changeSort = function(sort, column) {
|
||||
if (sort.column == column) {
|
||||
sort.reverse = !sort.reverse;
|
||||
} else {
|
||||
sort.column = column;
|
||||
sort.reverse = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
$scope.$close(null);
|
||||
};
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
<div class="console" ng-controller="ConsoleController">
|
||||
<div class="console-part-log">
|
||||
<div class="console-log-outer" ui-scroll-down-on="log.lines.length">
|
||||
<ul>
|
||||
<li ng-repeat="line in log.lines track by $index">{{line}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="console-part-input">
|
||||
<div class="console-input-left">
|
||||
</div>
|
||||
<div class="console-input-middle">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon">@</span>
|
||||
<input type="text" class="form-control" placeholder="Command" ng-model="commandText" ui-enter="issueCommand()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="console-input-right">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default">A</button>
|
||||
<button type="button" class="btn btn-default">B</button>
|
||||
<button type="button" class="btn btn-default">C</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.console', [
|
||||
'xe.log'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('ConsoleController', function($scope, log) {
|
||||
$scope.log = log;
|
||||
|
||||
$scope.commandText = '';
|
||||
|
||||
$scope.issueCommand = function() {
|
||||
var command = $scope.commandText;
|
||||
$scope.commandText = '';
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.appendLine('@' + command);
|
||||
|
||||
// TODO(benvanik): execute.
|
||||
console.log(command);
|
||||
};
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
TODO: GPU
|
|
@ -1 +0,0 @@
|
|||
TODO: kernel
|
|
@ -1 +0,0 @@
|
|||
TODO: memory
|
|
@ -1,40 +0,0 @@
|
|||
<nav class="navbar navbar-default" role="navigation" ng-controller="NavbarController">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="http://xenia.jp/" target="_blank">Xenia</a>
|
||||
</div>
|
||||
<ul class="nav navbar-nav" ng-show="app.session">
|
||||
<li ng-class="{ active: $state.includes('session.code') }"><a ui-sref="session.code">Code</a></li>
|
||||
<li ng-class="{ active: $state.includes('session.memory') }"><a ui-sref="session.memory">Memory</a></li>
|
||||
<li ng-class="{ active: $state.includes('session.kernel') }"><a ui-sref="session.kernel">Kernel</a></li>
|
||||
<li ng-class="{ active: $state.includes('session.gpu') }"><a ui-sref="session.gpu">GPU</a></li>
|
||||
<li ng-class="{ active: $state.includes('session.apu') }"><a ui-sref="session.apu">APU</a></li>
|
||||
</ul>
|
||||
<p class="navbar-text navbar-right"></p>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<div class="btn-group navbar-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="connect()" ng-disabled="app.loading">
|
||||
<span class="glyphicon glyphicon-link"></span> Connect
|
||||
</button>
|
||||
<!--<button type="button" class="btn btn-default" ng-click="open()" ng-disabled="app.loading">
|
||||
<span class="glyphicon glyphicon-file"></span> Open
|
||||
</button>-->
|
||||
<button type="button" class="btn btn-default" ng-click="refresh()" ng-disabled="app.loading">
|
||||
<span class="glyphicon glyphicon-refresh"></span> Refresh
|
||||
</button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" ng-disabled="app.loading">
|
||||
<span class="glyphicon glyphicon-cog"></span> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
<li><a href="#">Something else here</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#">Separated link</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="navbar-text navbar-right"></p>
|
||||
<p class="navbar-text navbar-right" ng-show="app.session">{{app.session.source}}</p>
|
||||
</nav>
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.ui.navbar', []);
|
||||
|
||||
|
||||
module.controller('NavbarController', function(
|
||||
$rootScope, $scope, $state, app, log) {
|
||||
|
||||
$scope.refresh = function() {
|
||||
$rootScope.$emit('refresh');
|
||||
};
|
||||
|
||||
$scope.connect = function() {
|
||||
// TODO(benvanik): show a fancy dialog or something.
|
||||
var oldSession = app.session;
|
||||
app.connect().then(function(session) {
|
||||
if (!oldSession || oldSession.id != session.id) {
|
||||
$state.go('session', {
|
||||
'sessionId': session.id
|
||||
}, {
|
||||
notify: true
|
||||
});
|
||||
}
|
||||
}, function(e) {
|
||||
$state.go('/', {
|
||||
}, {
|
||||
notify: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.open = function() {
|
||||
var inputEl = document.createElement('input');
|
||||
inputEl.type = 'file';
|
||||
inputEl.accept = '.xe-trace,application/x-extension-xe-trace';
|
||||
inputEl.onchange = function(e) {
|
||||
$scope.$apply(function() {
|
||||
if (inputEl.files.length) {
|
||||
//app.open(inputEl.files[0]);
|
||||
log.info('Not implemented yet');
|
||||
}
|
||||
});
|
||||
};
|
||||
inputEl.click();
|
||||
};
|
||||
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
<div ui-view></div>
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html ng-app="app">
|
||||
<head>
|
||||
<title>Xenia Debugger</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
|
||||
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.7.0.js"></script>
|
||||
<script src="//angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="//codemirror.net/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="assets/styles/app.css">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body ng-controller="AppController">
|
||||
<div class="app-main" ng-class="{disconnected: !app.session.dataSource || !app.session.dataSource.online}">
|
||||
<div class="app-header" ng-include="'assets/ui/navbar.html'"></div>
|
||||
<div class="app-body tab-content" ui-view></div>
|
||||
<div class="app-console navbar-default" ng-include="'assets/ui/console/console.html'"></div>
|
||||
</div>
|
||||
|
||||
<script src="http://codemirror.net/lib/codemirror.js"></script>
|
||||
|
||||
<script src="src/base.js"></script>
|
||||
|
||||
<script src="src/app.js"></script>
|
||||
<script src="src/datasources.js"></script>
|
||||
<script src="src/directives.js"></script>
|
||||
<script src="src/filters.js"></script>
|
||||
<script src="src/log.js"></script>
|
||||
<script src="src/router.js"></script>
|
||||
<script src="src/session.js"></script>
|
||||
<script src="assets/ui/navbar.js"></script>
|
||||
<script src="assets/ui/console/console.js"></script>
|
||||
<script src="assets/ui/code/code-tab.js"></script>
|
||||
<script src="assets/ui/code/function-view.js"></script>
|
||||
<script src="assets/ui/code/module-info.js"></script>
|
||||
<script src="assets/ui/code/thread-info.js"></script>
|
||||
|
||||
<script src="debugger.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,125 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('app', [
|
||||
'ui.bootstrap',
|
||||
'ui.router',
|
||||
'xe.datasources',
|
||||
'xe.directives',
|
||||
'xe.filters',
|
||||
'xe.log',
|
||||
'xe.router',
|
||||
'xe.session',
|
||||
'xe.ui.code',
|
||||
'xe.ui.code.functionView',
|
||||
'xe.ui.code.moduleInfo',
|
||||
'xe.ui.code.threadInfo',
|
||||
'xe.ui.console',
|
||||
'xe.ui.navbar'
|
||||
]);
|
||||
|
||||
|
||||
module.controller('AppController', function($scope, app) {
|
||||
this.app = app;
|
||||
});
|
||||
|
||||
|
||||
module.service('app', function(
|
||||
$rootScope, $q, $state, log, Session) {
|
||||
var App = function() {
|
||||
this.loading = false;
|
||||
this.session = null;
|
||||
};
|
||||
|
||||
App.prototype.setSession = function(session) {
|
||||
this.close();
|
||||
|
||||
this.session = session;
|
||||
$rootScope.$emit('refresh');
|
||||
};
|
||||
|
||||
App.prototype.close = function() {
|
||||
this.loading = false;
|
||||
if (this.session) {
|
||||
this.session.dispose();
|
||||
this.session = null;
|
||||
}
|
||||
};
|
||||
|
||||
App.prototype.open = function(sessionId) {
|
||||
var d = $q.defer();
|
||||
|
||||
// Ignore if already open.
|
||||
if (this.session && this.session.id == sessionId) {
|
||||
d.resolve(this.session);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Close existing.
|
||||
this.close();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
log.info('Opening session ' + sessionId);
|
||||
|
||||
// Open session.
|
||||
var session = new Session(sessionId);
|
||||
this.loading = false;
|
||||
this.setSession(session);
|
||||
d.resolve(session);
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
App.prototype.connect = function(opt_host) {
|
||||
this.close();
|
||||
|
||||
var d = $q.defer();
|
||||
this.loading = true;
|
||||
|
||||
Session.query(opt_host).then((function(infos) {
|
||||
var info = infos[0];
|
||||
var id = info.titleId;
|
||||
if (id == '00000000') {
|
||||
id = info.name;
|
||||
}
|
||||
var session = new Session(id);
|
||||
var p = session.connect(opt_host);
|
||||
p.then((function(session) {
|
||||
this.loading = false;
|
||||
this.setSession(session);
|
||||
d.resolve(session);
|
||||
}).bind(this), (function(e) {
|
||||
this.loading = false;
|
||||
d.reject(e);
|
||||
}).bind(this), function(update) {
|
||||
d.notify(update);
|
||||
});
|
||||
}).bind(this), (function(e) {
|
||||
this.loading = false;
|
||||
log.info('No sessions found at ' + Session.getHost(opt_host));
|
||||
d.reject(e);
|
||||
}).bind(this));
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
return new App();
|
||||
});
|
||||
|
||||
|
||||
module.run(function($rootScope, $state, $stateParams, app, log) {
|
||||
$rootScope.$state = $state;
|
||||
$rootScope.$stateParams = $stateParams;
|
||||
|
||||
$rootScope.app = app;
|
||||
$rootScope.log = log;
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
function inherits(childCtor, parentCtor) {
|
||||
function tempCtor() {};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
childCtor.superClass_ = parentCtor.prototype;
|
||||
childCtor.prototype = new tempCtor();
|
||||
childCtor.prototype.constructor = childCtor;
|
||||
};
|
||||
|
||||
var EventEmitter = function() {
|
||||
this.events_ = {};
|
||||
};
|
||||
EventEmitter.prototype.dispose = function() {
|
||||
this.events_ = {};
|
||||
};
|
||||
EventEmitter.prototype.on = function(name, listener, opt_scope) {
|
||||
var listeners = this.events_[name];
|
||||
if (!listeners) {
|
||||
listeners = this.events_[name] = [];
|
||||
}
|
||||
listeners.push({
|
||||
callback: listener,
|
||||
scope: opt_scope || null
|
||||
});
|
||||
};
|
||||
EventEmitter.prototype.off = function(name, listener, opt_scope) {
|
||||
var listeners = this.events_[name];
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
for (var n = 0; n < listeners.length; n++) {
|
||||
if (listeners[n].callback == listener) {
|
||||
listeners.splice(n, 1);
|
||||
if (!listeners.length) {
|
||||
delete this.events_[name];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
EventEmitter.prototype.emit = function(name, args) {
|
||||
var listeners = this.events_[name];
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
for (var n = 0; n < listeners.length; n++) {
|
||||
var listener = listeners[n];
|
||||
listener.callback.apply(listener.scope, args);
|
||||
}
|
||||
};
|
|
@ -1,317 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.datasources', []);
|
||||
|
||||
|
||||
module.service('Breakpoint', function() {
|
||||
// http://stackoverflow.com/a/2117523/377392
|
||||
var uuidFormat = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
||||
function uuid4() {
|
||||
return uuidFormat.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
};
|
||||
|
||||
var Breakpoint = function(opt_id) {
|
||||
this.id = opt_id || uuid4();
|
||||
this.type = Breakpoint.Type.TEMP;
|
||||
this.fnAddress = 0;
|
||||
this.address = 0;
|
||||
this.enabled = true;
|
||||
};
|
||||
Breakpoint.Type = {
|
||||
TEMP: 'temp',
|
||||
CODE: 'code'
|
||||
};
|
||||
Breakpoint.fromJSON = function(json) {
|
||||
var breakpoint = new Breakpoint(json.id);
|
||||
breakpoint.type = json.type;
|
||||
breakpoint.fnAddress = json.fnAddress;
|
||||
breakpoint.address = json.address;
|
||||
breakpoint.enabled = json.enabled;
|
||||
return breakpoint;
|
||||
};
|
||||
Breakpoint.prototype.toJSON = function() {
|
||||
return {
|
||||
'id': this.id,
|
||||
'type': this.type,
|
||||
'fnAddress': this.fnAddress,
|
||||
'address': this.address,
|
||||
'enabled': this.enabled
|
||||
};
|
||||
};
|
||||
return Breakpoint;
|
||||
});
|
||||
|
||||
|
||||
module.service('DataSource', function($q) {
|
||||
var DataSource = function(source, delegate) {
|
||||
EventEmitter.call(this);
|
||||
this.source = source;
|
||||
this.delegate = delegate;
|
||||
this.online = false;
|
||||
this.status = 'disconnected';
|
||||
};
|
||||
inherits(DataSource, EventEmitter);
|
||||
DataSource.prototype.open = function() {};
|
||||
DataSource.prototype.dispose = function() {};
|
||||
DataSource.prototype.issue = function(command) {};
|
||||
|
||||
DataSource.prototype.makeReady = function() {
|
||||
return this.issue({
|
||||
command: 'debug.make_ready'
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.getModuleList = function() {
|
||||
return this.issue({
|
||||
command: 'cpu.get_module_list'
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.getModule = function(moduleName) {
|
||||
return this.issue({
|
||||
command: 'cpu.get_module',
|
||||
module: moduleName
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.getFunctionList = function(moduleName, opt_since) {
|
||||
return this.issue({
|
||||
command: 'cpu.get_function_list',
|
||||
module: moduleName,
|
||||
since: opt_since || 0
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.getFunction = function(address) {
|
||||
return this.issue({
|
||||
command: 'cpu.get_function',
|
||||
address: address
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.getThreadStates = function() {
|
||||
return this.issue({
|
||||
command: 'cpu.get_thread_states'
|
||||
});
|
||||
};
|
||||
|
||||
// set registers/etc?
|
||||
|
||||
DataSource.prototype.addBreakpoint = function(breakpoint) {
|
||||
return this.addBreakpoints([breakpoint]);
|
||||
};
|
||||
|
||||
DataSource.prototype.addBreakpoints = function(breakpoints) {
|
||||
if (!breakpoints.length) {
|
||||
var d = $q.defer();
|
||||
d.resolve();
|
||||
return d.promise;
|
||||
}
|
||||
return this.issue({
|
||||
command: 'cpu.add_breakpoints',
|
||||
breakpoints: breakpoints.map(function(breakpoint) {
|
||||
return breakpoint.toJSON();
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.removeBreakpoint = function(breakpointId) {
|
||||
return this.removeBreakpoints([breakpointId]);
|
||||
};
|
||||
|
||||
DataSource.prototype.removeBreakpoints = function(breakpointIds) {
|
||||
return this.issue({
|
||||
command: 'cpu.remove_breakpoints',
|
||||
breakpointIds: breakpointIds
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.removeAllBreakpoints = function() {
|
||||
return this.issue({
|
||||
command: 'cpu.remove_all_breakpoints'
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.continueExecution = function() {
|
||||
return this.issue({
|
||||
command: 'cpu.continue'
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.breakExecution = function() {
|
||||
return this.issue({
|
||||
command: 'cpu.break'
|
||||
});
|
||||
};
|
||||
|
||||
DataSource.prototype.stepNext = function(threadId) {
|
||||
return this.issue({
|
||||
command: 'cpu.step',
|
||||
threadId: threadId
|
||||
});
|
||||
};
|
||||
|
||||
return DataSource;
|
||||
});
|
||||
|
||||
module.service('RemoteDataSource', function(
|
||||
$rootScope, $q, log, DataSource) {
|
||||
var RemoteDataSource = function(url, delegate) {
|
||||
DataSource.call(this, url, delegate);
|
||||
this.url = url;
|
||||
this.socket = null;
|
||||
this.nextRequestId_ = 1;
|
||||
this.pendingRequests_ = {};
|
||||
};
|
||||
inherits(RemoteDataSource, DataSource);
|
||||
|
||||
RemoteDataSource.prototype.open = function() {
|
||||
var url = this.url;
|
||||
|
||||
this.online = false;
|
||||
this.status = 'connecting';
|
||||
|
||||
var d = $q.defer();
|
||||
|
||||
this.socket = new WebSocket(url, []);
|
||||
this.socket.onopen = (function() {
|
||||
$rootScope.$apply((function() {
|
||||
// TODO(benvanik): handshake
|
||||
|
||||
this.online = true;
|
||||
this.status = 'connected';
|
||||
this.emit('online');
|
||||
d.resolve();
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
|
||||
this.socket.onclose = (function(e) {
|
||||
$rootScope.$apply((function() {
|
||||
this.online = false;
|
||||
if (this.status == 'connecting') {
|
||||
this.status = 'disconnected';
|
||||
d.reject(e.code + ' ' + e.reason);
|
||||
} else {
|
||||
this.status = 'disconnected';
|
||||
log.info('Disconnected');
|
||||
this.emit('offline');
|
||||
}
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
|
||||
this.socket.onerror = (function(e) {
|
||||
// ?
|
||||
}).bind(this);
|
||||
|
||||
this.socket.onmessage = (function(e) {
|
||||
$rootScope.$apply((function() {
|
||||
this.socketMessage(e);
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
RemoteDataSource.prototype.socketMessage = function(e) {
|
||||
console.log('message', e.data);
|
||||
var json = JSON.parse(e.data);
|
||||
if (json.requestId) {
|
||||
// Response to a previous request.
|
||||
var request = this.pendingRequests_[json.requestId];
|
||||
if (request) {
|
||||
delete this.pendingRequests_[json.requestId];
|
||||
if (json.status) {
|
||||
request.deferred.resolve(json.result);
|
||||
} else {
|
||||
request.deferred.reject(json.result);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notification.
|
||||
switch (json.type) {
|
||||
case 'breakpoint':
|
||||
this.delegate.onBreakpointHit(
|
||||
json.breakpointId, json.threadId);
|
||||
break;
|
||||
default:
|
||||
log.error('Unknown notification type: ' + json.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RemoteDataSource.prototype.dispose = function() {
|
||||
this.pendingRequests_ = {};
|
||||
this.online = false;
|
||||
this.status = 'disconnected';
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
this.socket = null;
|
||||
}
|
||||
DataSource.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
RemoteDataSource.prototype.issue = function(command) {
|
||||
var d = $q.defer();
|
||||
command.requestId = this.nextRequestId_++;
|
||||
this.socket.send(JSON.stringify(command));
|
||||
command.deferred = d;
|
||||
this.pendingRequests_[command.requestId] = command;
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
return RemoteDataSource;
|
||||
});
|
||||
|
||||
|
||||
module.service('FileDataSource', function($q, DataSource) {
|
||||
var FileDataSource = function(file, delegate) {
|
||||
DataSource.call(this, file.name, delegate);
|
||||
this.file = file;
|
||||
};
|
||||
inherits(FileDataSource, DataSource);
|
||||
|
||||
FileDataSource.prototype.open = function() {
|
||||
this.status = 'connecting';
|
||||
|
||||
var d = $q.defer();
|
||||
|
||||
var self = this;
|
||||
window.setTimeout(function() {
|
||||
$scope.$apply((function() {
|
||||
// TODO(benvanik): scan/load trace
|
||||
|
||||
this.online = true;
|
||||
this.status = 'connected';
|
||||
d.resolve();
|
||||
}).bind(self));
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
FileDataSource.prototype.dispose = function() {
|
||||
this.online = false;
|
||||
if (this.file) {
|
||||
if (this.file.close) {
|
||||
this.file.close();
|
||||
}
|
||||
this.file = null;
|
||||
}
|
||||
DataSource.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
return FileDataSource;
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.directives', [
|
||||
'ui.router'
|
||||
]);
|
||||
|
||||
|
||||
module.directive('uiEnter', function() {
|
||||
return function($scope, element, attrs) {
|
||||
element.bind('keydown keypress', function(e) {
|
||||
if(e.which === 13) {
|
||||
$scope.$apply(function(){
|
||||
$scope.$eval(attrs.uiEnter);
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('uiEscape', function() {
|
||||
return function($scope, element, attrs) {
|
||||
element.bind('keydown keypress', function(e) {
|
||||
if(e.which === 27) {
|
||||
$scope.$apply(function(){
|
||||
$scope.$eval(attrs.uiEscape);
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('uiScrollDownOn', function() {
|
||||
return {
|
||||
priority: 1,
|
||||
link: function($scope, element, attrs) {
|
||||
$scope.$watch(attrs.uiScrollDownOn, function() {
|
||||
element[0].scrollTop = element[0].scrollHeight;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('xeCoderef', function($state) {
|
||||
return {
|
||||
priority: 1,
|
||||
link: function($scope, element, attrs) {
|
||||
var target = attrs.xeCoderef;
|
||||
var stateName = 'session.code.function';
|
||||
var stateParams = {
|
||||
function: target,
|
||||
a: null
|
||||
};
|
||||
element.attr('href', $state.href(stateName, stateParams));
|
||||
element.bind('click', function(e) {
|
||||
e.preventDefault();
|
||||
$state.go(stateName, stateParams);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('xeMemref', function($state) {
|
||||
return {
|
||||
priority: 1,
|
||||
link: function($scope, element, attrs) {
|
||||
var target = attrs.xeMemref;
|
||||
var stateName = 'session.memory';
|
||||
var stateParams = {
|
||||
a: target
|
||||
};
|
||||
element.attr('href', $state.href(stateName, stateParams));
|
||||
element.bind('click', function(e) {
|
||||
e.preventDefault();
|
||||
$state.go(stateName, stateParams);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.filters', []);
|
||||
|
||||
|
||||
module.filter('hex16', function() {
|
||||
return function(number) {
|
||||
if (number !== null && number !== undefined) {
|
||||
var str = '' + number.toString(16).toUpperCase();
|
||||
while (str.length < 4) str = '0' + str;
|
||||
return str;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.filter('hex32', function() {
|
||||
return function(number) {
|
||||
if (number !== null && number !== undefined) {
|
||||
var str = '' + number.toString(16).toUpperCase();
|
||||
while (str.length < 8) str = '0' + str;
|
||||
return str;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.filter('exp8', function() {
|
||||
return function(number) {
|
||||
if (number !== null && number !== undefined) {
|
||||
var str = number.toExponential(8);
|
||||
if (number >= 0) {
|
||||
str = ' ' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.log', []);
|
||||
|
||||
|
||||
module.service('log', function($rootScope) {
|
||||
var Log = function() {
|
||||
this.lines = [];
|
||||
|
||||
this.progressActive = false;
|
||||
this.progress = 0;
|
||||
};
|
||||
|
||||
Log.prototype.appendLine = function(line) {
|
||||
this.lines.push(line);
|
||||
};
|
||||
|
||||
Log.prototype.info = function(line) {
|
||||
this.appendLine('I ' + line);
|
||||
};
|
||||
|
||||
Log.prototype.error = function(line) {
|
||||
this.appendLine('E ' + line);
|
||||
};
|
||||
|
||||
Log.prototype.setProgress = function(value) {
|
||||
this.progressActive = true;
|
||||
this.progress = value;
|
||||
};
|
||||
|
||||
Log.prototype.clearProgress = function() {
|
||||
this.progressActive = false;
|
||||
};
|
||||
|
||||
return new Log();
|
||||
});
|
|
@ -1,146 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.router', [
|
||||
'ui.router',
|
||||
]);
|
||||
|
||||
|
||||
module.config(function($stateProvider, $urlRouterProvider) {
|
||||
$urlRouterProvider.otherwise('/');
|
||||
|
||||
$stateProvider.state('/', {
|
||||
template: 'empty'
|
||||
});
|
||||
|
||||
$stateProvider.state('session', {
|
||||
url: '/:sessionId',
|
||||
templateUrl: 'assets/ui/session.html',
|
||||
resolve: {
|
||||
app: 'app',
|
||||
session: function($stateParams, $state, $q,
|
||||
Session, app) {
|
||||
// If we are given a session we assume the user is trying to connect to
|
||||
// it. Attempt that now. If we fail we redirect to home, otherwise we
|
||||
// check whether it's the same game down below.
|
||||
var d = $q.defer();
|
||||
if ($stateParams.sessionId) {
|
||||
if (!app.session ||
|
||||
app.session.id != $stateParams.sessionId) {
|
||||
Session.query().then(function(infos) {
|
||||
var id = (infos[0].titleId == '00000000') ?
|
||||
infos[0].name : infos[0].titleId;
|
||||
if (!app.session || app.session.id == id) {
|
||||
// Same session, continue.
|
||||
var p = app.connect();
|
||||
p.then(function(session) {
|
||||
d.resolve(session);
|
||||
}, function(e) {
|
||||
$state.go('session', {
|
||||
'sessionId': session.id
|
||||
}, {
|
||||
notify: true
|
||||
});
|
||||
d.reject(e);
|
||||
})
|
||||
} else {
|
||||
// Different session. Create without connection.
|
||||
var p = app.open(id);
|
||||
p.then(function(session) {
|
||||
d.resolve(session);
|
||||
}, function(e) {
|
||||
d.reject(e);
|
||||
});
|
||||
}
|
||||
}, function(e) {
|
||||
var p = app.open($stateParams.sessionId);
|
||||
p.then(function(session) {
|
||||
d.resolve(session);
|
||||
}, function(e) {
|
||||
d.reject(e);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var p = app.open($stateParams.sessionId);
|
||||
p.then(function(session) {
|
||||
d.resolve(session);
|
||||
}, function(e) {
|
||||
d.reject(e);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
d.resolve(null);
|
||||
}
|
||||
return d.promise;
|
||||
}
|
||||
},
|
||||
controller: function($scope, $stateParams, $q, app, session) {
|
||||
},
|
||||
onEnter: function() {
|
||||
},
|
||||
onExit: function() {}
|
||||
});
|
||||
|
||||
$stateProvider.state('session.code', {
|
||||
url: '/code',
|
||||
templateUrl: 'assets/ui/code/code-tab.html',
|
||||
controller: function($stateParams) {
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
$stateProvider.state('session.code.function', {
|
||||
url: '/:function?a',
|
||||
templateUrl: 'assets/ui/code/function-view.html',
|
||||
controller: function($scope, $stateParams) {
|
||||
$scope.functionAddress = parseInt($stateParams.function, 16);
|
||||
$scope.highlightAddress = parseInt($stateParams.a, 16);
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
|
||||
$stateProvider.state('session.memory', {
|
||||
url: '/memory?a',
|
||||
templateUrl: 'assets/ui/memory/memory-tab.html',
|
||||
controller: function($stateParams) {
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
|
||||
$stateProvider.state('session.kernel', {
|
||||
url: '/kernel',
|
||||
templateUrl: 'assets/ui/kernel/kernel-tab.html',
|
||||
controller: function($stateParams) {
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
|
||||
$stateProvider.state('session.gpu', {
|
||||
url: '/gpu',
|
||||
templateUrl: 'assets/ui/gpu/gpu-tab.html',
|
||||
controller: function($stateParams) {
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
|
||||
$stateProvider.state('session.apu', {
|
||||
url: '/apu',
|
||||
templateUrl: 'assets/ui/apu/apu-tab.html',
|
||||
controller: function($stateParams) {
|
||||
},
|
||||
onEnter: function() {},
|
||||
onExit: function() {}
|
||||
});
|
||||
});
|
|
@ -1,489 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('xe.session', []);
|
||||
|
||||
|
||||
module.service('Session', function(
|
||||
$rootScope, $q, $http, $state, log,
|
||||
Breakpoint, FileDataSource, RemoteDataSource) {
|
||||
var State = function(session) {
|
||||
this.session = session;
|
||||
this.clear();
|
||||
};
|
||||
State.prototype.clear = function() {
|
||||
this.cache_ = {
|
||||
moduleList: [],
|
||||
modules: {},
|
||||
moduleFunctionLists: {},
|
||||
functions: {},
|
||||
threadStates: {},
|
||||
threadList: []
|
||||
};
|
||||
};
|
||||
State.prototype.sync = function() {
|
||||
var cache = this.cache_;
|
||||
var dataSource = this.session.dataSource;
|
||||
if (!dataSource) {
|
||||
var d = $q.defer();
|
||||
d.resolve();
|
||||
return d.promise;
|
||||
}
|
||||
var ps = [];
|
||||
|
||||
// Update all modules/functions.
|
||||
var modulesUpdated = $q.defer();
|
||||
ps.push(modulesUpdated.promise);
|
||||
dataSource.getModuleList().then((function(list) {
|
||||
cache.moduleList = list;
|
||||
|
||||
// Update module information.
|
||||
var moduleFetches = [];
|
||||
list.forEach(function(module) {
|
||||
if (cache.modules[module.name]) {
|
||||
return;
|
||||
}
|
||||
var moduleFetch = $q.defer();
|
||||
moduleFetches.push(moduleFetch.promise);
|
||||
dataSource.getModule(module.name).
|
||||
then(function(moduleInfo) {
|
||||
cache.modules[module.name] = moduleInfo;
|
||||
moduleFetch.resolve();
|
||||
}, function(e) {
|
||||
moduleFetch.reject(e);
|
||||
});
|
||||
});
|
||||
|
||||
// Update function lists for each module.
|
||||
list.forEach(function(module) {
|
||||
var cached = cache.moduleFunctionLists[module.name];
|
||||
var functionListFetch = $q.defer();
|
||||
moduleFetches.push(functionListFetch);
|
||||
dataSource.getFunctionList(module.name, cached ? cached.version : 0).
|
||||
then(function(result) {
|
||||
if (cached) {
|
||||
cached.version = result.version;
|
||||
for (var n = 0; n < result.list.length; n++) {
|
||||
cached.list.push(result.list[n]);
|
||||
}
|
||||
} else {
|
||||
cached = cache.moduleFunctionLists[module.name] = {
|
||||
version: result.version,
|
||||
list: result.list
|
||||
};
|
||||
}
|
||||
functionListFetch.resolve();
|
||||
}, function(e) {
|
||||
functionListFetch.reject(e);
|
||||
});
|
||||
});
|
||||
|
||||
$q.all(moduleFetches).then(function() {
|
||||
modulesUpdated.resolve();
|
||||
}, function(e) {
|
||||
modulesUpdated.reject();
|
||||
});
|
||||
}).bind(this), function(e) {
|
||||
modulesUpdated.reject(e);
|
||||
});
|
||||
|
||||
// Update threads/thread states.
|
||||
var threadsUpdated = $q.defer();
|
||||
ps.push(threadsUpdated.promise);
|
||||
dataSource.getThreadStates().then((function(states) {
|
||||
cache.threadStates = states;
|
||||
cache.threadList = [];
|
||||
for (var threadId in states) {
|
||||
cache.threadList.push(states[threadId]);
|
||||
}
|
||||
threadsUpdated.resolve();
|
||||
}).bind(this), function(e) {
|
||||
threadsUpdated.reject(e);
|
||||
});
|
||||
|
||||
var d = $q.defer();
|
||||
$q.all(ps).then((function() {
|
||||
d.resolve();
|
||||
}).bind(this), (function(e) {
|
||||
d.reject(e);
|
||||
}).bind(this));
|
||||
return d.promise;
|
||||
};
|
||||
State.prototype.getModuleList = function() {
|
||||
return this.cache_.moduleList;
|
||||
};
|
||||
State.prototype.getModule = function(moduleName) {
|
||||
return this.cache_.modules[moduleName] || null;
|
||||
};
|
||||
State.prototype.getFunctionList = function(moduleName) {
|
||||
var cached = this.cache_.moduleFunctionLists[moduleName];
|
||||
return cached ? cached.list : [];
|
||||
};
|
||||
State.prototype.getFunction = function(address) {
|
||||
return this.cache_.functions[address] || null;
|
||||
};
|
||||
State.prototype.fetchFunction = function(address) {
|
||||
var cache = this.cache_;
|
||||
var d = $q.defer();
|
||||
var cached = cache.functions[address];
|
||||
if (cached) {
|
||||
d.resolve(cached);
|
||||
return d.promise;
|
||||
}
|
||||
var dataSource = this.session.dataSource;
|
||||
if (!dataSource) {
|
||||
d.reject(new Error('Not online.'));
|
||||
return d.promise;
|
||||
}
|
||||
dataSource.getFunction(address).then(function(result) {
|
||||
cache.functions[address] = result;
|
||||
d.resolve(result);
|
||||
}, function(e) {
|
||||
d.reject(e);
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
Object.defineProperty(State.prototype, 'threadList', {
|
||||
get: function() {
|
||||
return this.cache_.threadList || [];
|
||||
}
|
||||
});
|
||||
State.prototype.getThreadStates = function() {
|
||||
return this.cache_.threadStates || {};
|
||||
};
|
||||
State.prototype.getThreadState = function(threadId) {
|
||||
return this.cache_.threadStates[threadId] || null;
|
||||
};
|
||||
|
||||
var Session = function(id, opt_dataSource) {
|
||||
this.id = id;
|
||||
|
||||
this.breakpoints = {};
|
||||
this.breakpointsById = {};
|
||||
|
||||
this.dataSource = opt_dataSource || null;
|
||||
this.state = new State(this);
|
||||
|
||||
this.activeThread = null;
|
||||
|
||||
this.paused = false;
|
||||
|
||||
this.loadState();
|
||||
};
|
||||
|
||||
Session.prototype.dispose = function() {
|
||||
this.saveState();
|
||||
this.disconnect();
|
||||
};
|
||||
|
||||
Session.prototype.loadState = function() {
|
||||
var raw = window.localStorage[this.id];
|
||||
if (!raw) {
|
||||
return;
|
||||
}
|
||||
var json = JSON.parse(raw);
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
|
||||
var breakpointList = json.breakpoints;
|
||||
this.breakpoints = {};
|
||||
for (var n = 0; n < breakpointList.length; n++) {
|
||||
var breakpointJson = breakpointList[n];
|
||||
var breakpoint = Breakpoint.fromJSON(breakpointJson);
|
||||
this.breakpoints[breakpointJson.address] = breakpoint;
|
||||
this.breakpointsById[breakpoint.id] = breakpoint;
|
||||
}
|
||||
};
|
||||
|
||||
Session.prototype.saveState = function() {
|
||||
var json = {
|
||||
id: this.id,
|
||||
breakpoints: []
|
||||
};
|
||||
for (var key in this.breakpointsById) {
|
||||
var breakpoint = this.breakpointsById[key];
|
||||
if (breakpoint.type != Breakpoint.TEMP) {
|
||||
json.breakpoints.push(breakpoint.toJSON());
|
||||
}
|
||||
}
|
||||
window.localStorage[this.id] = JSON.stringify(json);
|
||||
};
|
||||
|
||||
Session.DEFAULT_HOST = '127.0.0.1:6200';
|
||||
|
||||
Session.getHost = function(opt_host) {
|
||||
return opt_host || Session.DEFAULT_HOST;
|
||||
};
|
||||
|
||||
Session.query = function(opt_host) {
|
||||
var url = 'http://' + Session.getHost(opt_host);
|
||||
var p = $http({
|
||||
method: 'GET',
|
||||
url: url + '/sessions',
|
||||
cache: false,
|
||||
timeout: 500,
|
||||
responseType: 'json'
|
||||
});
|
||||
var d = $q.defer();
|
||||
p.then(function(response) {
|
||||
if (!response.data || !response.data.length) {
|
||||
d.reject(new Error('No session data'));
|
||||
return;
|
||||
}
|
||||
d.resolve(response.data);
|
||||
}, function(e) {
|
||||
d.reject(e);
|
||||
});
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
Session.prototype.connect = function(opt_host) {
|
||||
this.disconnect();
|
||||
|
||||
var url = 'ws://' + Session.getHost(opt_host);
|
||||
|
||||
log.info('Connecting to ' + url + '...');
|
||||
log.setProgress(0);
|
||||
|
||||
var d = $q.defer();
|
||||
|
||||
var dataSource = new RemoteDataSource(url, this);
|
||||
var p = dataSource.open();
|
||||
p.then((function() {
|
||||
log.info('Connected!');
|
||||
log.clearProgress();
|
||||
this.setDataSource(dataSource).then((function() {
|
||||
d.resolve(this);
|
||||
}).bind(this), (function(e) {
|
||||
d.reject(e);
|
||||
}).bind(this));
|
||||
}).bind(this), (function(e) {
|
||||
log.error('Unable to connect: ' + e);
|
||||
log.clearProgress();
|
||||
d.reject(e);
|
||||
}).bind(this), function(update) {
|
||||
log.setProgress(update.progress);
|
||||
d.notify(update);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
Session.prototype.disconnect = function() {
|
||||
this.setDataSource(null);
|
||||
};
|
||||
|
||||
Session.prototype.setDataSource = function(dataSource) {
|
||||
var self = this;
|
||||
|
||||
var d = $q.defer();
|
||||
if (this.dataSource) {
|
||||
this.dataSource.dispose();
|
||||
this.dataSource = null;
|
||||
}
|
||||
$rootScope.$emit('refresh');
|
||||
if (!dataSource) {
|
||||
d.resolve();
|
||||
return d.promise;
|
||||
}
|
||||
this.state.clear();
|
||||
this.activeThread = null;
|
||||
|
||||
this.dataSource = dataSource;
|
||||
this.dataSource.on('online', function() {
|
||||
//
|
||||
}, this);
|
||||
this.dataSource.on('offline', function() {
|
||||
this.setDataSource(null);
|
||||
}, this);
|
||||
|
||||
var ps = [];
|
||||
|
||||
// Add breakpoints.
|
||||
var breakpointList = [];
|
||||
for (var key in this.breakpoints) {
|
||||
var breakpoint = this.breakpoints[key];
|
||||
if (breakpoint.enabled) {
|
||||
breakpointList.push(breakpoint);
|
||||
}
|
||||
}
|
||||
ps.push(this.dataSource.addBreakpoints(breakpointList));
|
||||
|
||||
// Perform a full sync.
|
||||
var syncDeferred = $q.defer();
|
||||
ps.push(syncDeferred.promise);
|
||||
this.state.sync().then((function() {
|
||||
// Put a breakpoint at the entry point.
|
||||
// TODO(benvanik): make an option?
|
||||
var moduleList = this.state.getModuleList();
|
||||
if (!moduleList.length) {
|
||||
log.error('No modules found!');
|
||||
syncDeferred.reject(new Error('No modules found.'));
|
||||
return;
|
||||
}
|
||||
var moduleInfo = this.state.getModule(moduleList[0].name);
|
||||
if (!moduleInfo) {
|
||||
log.error('Main module not found!');
|
||||
syncDeferred.reject(new Error('Main module not found.'));
|
||||
return;
|
||||
}
|
||||
var entryPoint = moduleInfo.exeEntryPoint;
|
||||
self.addTempBreakpoint(entryPoint, entryPoint);
|
||||
|
||||
syncDeferred.resolve();
|
||||
}).bind(this), (function(e) {
|
||||
syncDeferred.reject(e);
|
||||
}).bind(this));
|
||||
|
||||
$q.all(ps).then((function() {
|
||||
this.dataSource.makeReady().then(function() {
|
||||
d.resolve();
|
||||
}, function(e) {
|
||||
log.error('Error making target ready: ' + e);
|
||||
d.reject(e);
|
||||
});
|
||||
}).bind(this), (function(e) {
|
||||
log.error('Errors preparing target: ' + e);
|
||||
this.disconnect();
|
||||
d.reject(e);
|
||||
}).bind(this));
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
Session.prototype.addBreakpoint = function(breakpoint) {
|
||||
this.breakpoints[breakpoint.address] = breakpoint;
|
||||
this.breakpointsById[breakpoint.id] = breakpoint;
|
||||
if (this.dataSource) {
|
||||
this.dataSource.addBreakpoint(breakpoint);
|
||||
}
|
||||
this.saveState();
|
||||
return breakpoint;
|
||||
};
|
||||
|
||||
Session.prototype.addTempBreakpoint = function(fnAddress, address) {
|
||||
var breakpoint = new Breakpoint();
|
||||
breakpoint.type = Breakpoint.Type.TEMP;
|
||||
breakpoint.fnAddress = fnAddress;
|
||||
breakpoint.address = address;
|
||||
breakpoint.enabled = true;
|
||||
return this.addBreakpoint(breakpoint);
|
||||
};
|
||||
|
||||
Session.prototype.addCodeBreakpoint = function(fnAddress, address) {
|
||||
var breakpoint = new Breakpoint();
|
||||
breakpoint.type = Breakpoint.Type.CODE;
|
||||
breakpoint.fnAddress = fnAddress;
|
||||
breakpoint.address = address;
|
||||
breakpoint.enabled = true;
|
||||
return this.addBreakpoint(breakpoint);
|
||||
};
|
||||
|
||||
Session.prototype.removeBreakpoint = function(breakpoint) {
|
||||
delete this.breakpoints[breakpoint.address];
|
||||
delete this.breakpointsById[breakpoint.id];
|
||||
if (this.dataSource) {
|
||||
this.dataSource.removeBreakpoint(breakpoint.id);
|
||||
}
|
||||
this.saveState();
|
||||
};
|
||||
|
||||
Session.prototype.toggleBreakpoint = function(breakpoint, enabled) {
|
||||
var oldEnabled = enabled;
|
||||
breakpoint.enabled = enabled;
|
||||
if (this.dataSource) {
|
||||
if (breakpoint.enabled) {
|
||||
this.dataSource.addBreakpoint(breakpoint);
|
||||
} else {
|
||||
this.dataSource.removeBreakpoint(breakpoint.id);
|
||||
}
|
||||
}
|
||||
this.saveState();
|
||||
};
|
||||
|
||||
Session.prototype.onBreakpointHit = function(breakpointId, threadId) {
|
||||
// Now paused!
|
||||
this.paused = true;
|
||||
|
||||
this.state.sync().then((function() {
|
||||
// Switch active thread.
|
||||
var thread = this.state.getThreadState(threadId);
|
||||
this.activeThread = thread;
|
||||
|
||||
if (!breakpointId) {
|
||||
// Just a general pause.
|
||||
log.info('Execution paused.');
|
||||
return;
|
||||
}
|
||||
|
||||
var breakpoint = this.breakpointsById[breakpointId];
|
||||
if (!breakpoint) {
|
||||
log.error('Breakpoint hit but not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(benvanik): stash current breakpoint/thread/etc.
|
||||
|
||||
log.info('Breakpoint hit at 0x' +
|
||||
breakpoint.address.toString(16).toUpperCase() + '.');
|
||||
|
||||
$state.go('session.code.function', {
|
||||
sessionId: this.id,
|
||||
function: breakpoint.fnAddress.toString(16).toUpperCase(),
|
||||
a: breakpoint.address.toString(16).toUpperCase()
|
||||
}, {
|
||||
notify: true,
|
||||
reloadOnSearch: false
|
||||
});
|
||||
}).bind(this), (function(e) {
|
||||
log.error('Unable to synchronize state,');
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
Session.prototype.continueExecution = function() {
|
||||
if (!this.dataSource) {
|
||||
return;
|
||||
}
|
||||
this.paused = false;
|
||||
this.dataSource.continueExecution().then(function() {
|
||||
log.info('Execution resumed.');
|
||||
}, function(e) {
|
||||
log.error('Unable to continue: ' + e);
|
||||
});
|
||||
};
|
||||
|
||||
Session.prototype.breakExecution = function() {
|
||||
if (!this.dataSource) {
|
||||
return;
|
||||
}
|
||||
this.paused = true;
|
||||
this.dataSource.breakExecution().then(function() {
|
||||
log.info('Execution paused.');
|
||||
$rootScope.$emit('refresh');
|
||||
}, function(e) {
|
||||
log.error('Unable to break: ' + e);
|
||||
});
|
||||
};
|
||||
|
||||
Session.prototype.stepNext = function(threadId) {
|
||||
if (!this.dataSource) {
|
||||
return;
|
||||
}
|
||||
this.paused = false;
|
||||
this.dataSource.stepNext(threadId).then(function() {
|
||||
}, function(e) {
|
||||
log.error('Unable to step: ' + e);
|
||||
});
|
||||
};
|
||||
|
||||
return Session;
|
||||
});
|
|
@ -9,19 +9,33 @@ video drivers for your card.
|
|||
### Windows
|
||||
|
||||
* Windows 8 or 8.1
|
||||
* Visual Studio 2013
|
||||
* Visual Studio 2013+
|
||||
* [Python 2.7](http://www.python.org/download/releases/2.7.6/)
|
||||
* If you are on Windows 8, you will also need the [Windows 8.1 SDK](http://msdn.microsoft.com/en-us/windows/desktop/bg162891)
|
||||
|
||||
Ensure Python is in your PATH (`C:\Python27\`).
|
||||
|
||||
I recommend using [Cmder](http://bliker.github.io/cmder/) for git and command
|
||||
line usage.
|
||||
|
||||
#### Debugging
|
||||
|
||||
VS behaves oddly with the debug paths. Open the xenia-run project properties
|
||||
VS behaves oddly with the debug paths. Open the xenia project properties
|
||||
and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
|
||||
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
|
||||
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
|
||||
|
||||
### OSX
|
||||
|
||||
* Mac OSX 10.9+
|
||||
* Xcode 5.1+
|
||||
|
||||
#### Debugging
|
||||
|
||||
Choose `Product > Scheme > Edit Scheme`. For xenia, alloy-sandbox, and the
|
||||
other executables select the Run action on the left and set
|
||||
`Options > Working Directory` to your root xenia/ git path.
|
||||
|
||||
## xenia-build
|
||||
|
||||
A simple build script is included to manage basic tasks such as building
|
||||
|
@ -44,7 +58,7 @@ keypresses:
|
|||
|
||||
#### setup
|
||||
|
||||
Run this on initial checkout to pull down all dependencies and setup LLVM.
|
||||
Run this on initial checkout to pull down all dependencies and submodules.
|
||||
|
||||
xb setup
|
||||
|
||||
|
@ -95,14 +109,8 @@ switching between the debug and release variants with `--debug`.
|
|||
To make life easier you can use `--flagfile=myflags.txt` to specify all
|
||||
arguments, including using `--target=my.xex` to pick an executable.
|
||||
|
||||
### xenia-info
|
||||
|
||||
Dumps information about a xex file.
|
||||
|
||||
./bin/xenia-info some.xex
|
||||
|
||||
### xenia-run
|
||||
### xenia
|
||||
|
||||
Runs a xex.
|
||||
|
||||
./bin/xenia-run some.xex
|
||||
./bin/xenia some.xex
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Style Guide
|
||||
|
||||
The style guide can be summed up as 'clang-format with the Google style set'.
|
||||
In addition, the [Google Style Guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml)
|
||||
is followed and cpplint is the source of truth.
|
||||
|
||||
Base rules:
|
||||
|
||||
* 80 column line length max
|
||||
* LF (Unix-style) line endings
|
||||
* 2-space soft tabs, no TABs!
|
||||
* [Google Style Guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml) for naming/casing/etc
|
||||
|
||||
Code that really breaks from the formatting rules will not be accepted, as then
|
||||
no one else can use clang-format on the code without also touching all your
|
||||
lines.
|
||||
|
||||
## Tools
|
||||
|
||||
### clang-format
|
||||
|
||||
clang-format with the Google style is used to format all files. I recommend
|
||||
installing/wiring it up to your editor of choice so that you don't even have to
|
||||
think about tabs and wrapping and such.
|
||||
|
||||
#### Visual Studio
|
||||
|
||||
Grab the official [experimental Visual Studio plugin](http://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
|
||||
whole doc and invoke it to get it all done.
|
||||
|
||||
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)
|
||||
package. Set it to use the Google style and format on save. Never think about
|
||||
tabs or linefeeds or whatever again.
|
||||
|
||||
### cpplint
|
||||
|
||||
TODO(benvanik): write a cool script to do this/editor plugins.
|
||||
In the future, the linter will run as a git commit hook and on travis.
|
|
@ -0,0 +1,6 @@
|
|||
All code (headers and sources) lives under this path, excluding third_party code.
|
||||
|
||||
* alloy: the dynamic recompiler that parses PPC and generates code.
|
||||
* poly: a lightweight cross-platform/compiler compatibility library.
|
||||
* xdb: xenia debugger library and UI.
|
||||
* xenia: emulator code.
|
|
@ -0,0 +1,29 @@
|
|||
alloy: small dynamic recompiler engine
|
||||
===
|
||||
|
||||
Alloy is a transpiler framework that allows for pluggable frontends for decoding
|
||||
guest machine instructions (such as PPC) and pluggable backends for generating
|
||||
host code (such as x64). It features an SSA IR designed to model machine
|
||||
instructions and vector operations and compilation passes that seek to efficiently
|
||||
optimize previously-optimized code.
|
||||
|
||||
Future versions will cache generated code across runs and enable offline
|
||||
precompilation.
|
||||
|
||||
Frontends
|
||||
---
|
||||
|
||||
Frontends are responsible for translating guest machine instructions into IR.
|
||||
Information about the guest machine and ABI is used to support efficient CPU
|
||||
state accesses and debug information.
|
||||
|
||||
* PPC64 with Altivec extenions
|
||||
|
||||
Backends
|
||||
---
|
||||
|
||||
A backend takes optimized IR and assembles an implementation-specific result.
|
||||
The backend is also responsible for executing the code it generates and supporting
|
||||
debugging features (such as breakpoints).
|
||||
|
||||
* x64: IA-64 with AVX2 code generator
|
|
@ -10,11 +10,8 @@
|
|||
#ifndef ALLOY_ALLOY_PRIVATE_H_
|
||||
#define ALLOY_ALLOY_PRIVATE_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
|
||||
DECLARE_bool(debug);
|
||||
DECLARE_bool(always_disasm);
|
||||
|
||||
|
@ -22,11 +19,6 @@ DECLARE_bool(validate_hir);
|
|||
|
||||
DECLARE_uint64(break_on_instruction);
|
||||
DECLARE_uint64(break_on_memory);
|
||||
|
||||
|
||||
namespace alloy {
|
||||
|
||||
} // namespace alloy
|
||||
|
||||
DECLARE_bool(break_on_debugbreak);
|
||||
|
||||
#endif // ALLOY_ALLOY_PRIVATE_H_
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/alloy.h>
|
||||
#include <alloy/alloy-private.h>
|
||||
#include "alloy/alloy.h"
|
||||
#include "alloy/alloy-private.h"
|
||||
|
||||
using namespace alloy;
|
||||
|
||||
|
||||
#if 0 && DEBUG
|
||||
#define DEFAULT_DEBUG_FLAG true
|
||||
#else
|
||||
|
@ -20,15 +19,17 @@ using namespace alloy;
|
|||
#endif
|
||||
|
||||
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
|
||||
"Allow debugging and retain debug information.");
|
||||
DEFINE_bool(always_disasm, false,
|
||||
"Allow debugging and retain debug information.");
|
||||
DEFINE_bool(
|
||||
always_disasm, false,
|
||||
"Always add debug info to functions, even when no debugger is attached.");
|
||||
|
||||
DEFINE_bool(validate_hir, false,
|
||||
"Perform validation checks on the HIR during compilation.");
|
||||
"Perform validation checks on the HIR during compilation.");
|
||||
|
||||
// Breakpoints:
|
||||
DEFINE_uint64(break_on_instruction, 0,
|
||||
"int3 before the given guest address is executed.");
|
||||
"int3 before the given guest address is executed.");
|
||||
DEFINE_uint64(break_on_memory, 0,
|
||||
"int3 on read/write to the given memory address.");
|
||||
"int3 on read/write to the given memory address.");
|
||||
DEFINE_bool(break_on_debugbreak, true, "int3 on JITed __debugbreak requests.");
|
||||
|
|
|
@ -10,22 +10,9 @@
|
|||
#ifndef ALLOY_ALLOY_H_
|
||||
#define ALLOY_ALLOY_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/runtime/function.h>
|
||||
#include <alloy/runtime/module.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
#include <alloy/tracing/tracing.h>
|
||||
|
||||
|
||||
// TODO(benvanik): based on platform/config/etc.
|
||||
#include <alloy/backend/ivm/ivm_backend.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
|
||||
} // namespace alloy
|
||||
|
||||
#include "alloy/runtime/function.h"
|
||||
#include "alloy/runtime/module.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "alloy/runtime/thread_state.h"
|
||||
|
||||
#endif // ALLOY_ALLOY_H_
|
||||
|
|
|
@ -7,15 +7,14 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/arena.h>
|
||||
#include "alloy/arena.h"
|
||||
|
||||
using namespace alloy;
|
||||
#include "poly/poly.h"
|
||||
|
||||
namespace alloy {
|
||||
|
||||
Arena::Arena(size_t chunk_size) :
|
||||
chunk_size_(chunk_size),
|
||||
head_chunk_(NULL), active_chunk_(NULL) {
|
||||
}
|
||||
Arena::Arena(size_t chunk_size)
|
||||
: chunk_size_(chunk_size), head_chunk_(nullptr), active_chunk_(nullptr) {}
|
||||
|
||||
Arena::~Arena() {
|
||||
Reset();
|
||||
|
@ -25,7 +24,7 @@ Arena::~Arena() {
|
|||
delete chunk;
|
||||
chunk = next;
|
||||
}
|
||||
head_chunk_ = NULL;
|
||||
head_chunk_ = nullptr;
|
||||
}
|
||||
|
||||
void Arena::Reset() {
|
||||
|
@ -48,7 +47,7 @@ void* Arena::Alloc(size_t size) {
|
|||
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
|
||||
Chunk* next = active_chunk_->next;
|
||||
if (!next) {
|
||||
XEASSERT(size < chunk_size_); // need to support larger chunks
|
||||
assert_true(size < chunk_size_, "need to support larger chunks");
|
||||
next = new Chunk(chunk_size_);
|
||||
active_chunk_->next = next;
|
||||
}
|
||||
|
@ -74,11 +73,11 @@ void* Arena::CloneContents() {
|
|||
}
|
||||
chunk = chunk->next;
|
||||
}
|
||||
void* result = xe_malloc(total_length);
|
||||
void* result = malloc(total_length);
|
||||
uint8_t* p = (uint8_t*)result;
|
||||
chunk = head_chunk_;
|
||||
while (chunk) {
|
||||
xe_copy_struct(p, chunk->buffer, chunk->offset);
|
||||
memcpy(p, chunk->buffer, chunk->offset);
|
||||
p += chunk->offset;
|
||||
if (chunk == active_chunk_) {
|
||||
break;
|
||||
|
@ -88,14 +87,15 @@ void* Arena::CloneContents() {
|
|||
return result;
|
||||
}
|
||||
|
||||
Arena::Chunk::Chunk(size_t chunk_size) :
|
||||
next(NULL),
|
||||
capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = (uint8_t*)xe_malloc(capacity);
|
||||
Arena::Chunk::Chunk(size_t chunk_size)
|
||||
: next(nullptr), capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = reinterpret_cast<uint8_t*>(malloc(capacity));
|
||||
}
|
||||
|
||||
Arena::Chunk::~Chunk() {
|
||||
if (buffer) {
|
||||
xe_free(buffer);
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,14 +10,13 @@
|
|||
#ifndef ALLOY_ARENA_H_
|
||||
#define ALLOY_ARENA_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace alloy {
|
||||
|
||||
|
||||
class Arena {
|
||||
public:
|
||||
public:
|
||||
Arena(size_t chunk_size = 4 * 1024 * 1024);
|
||||
~Arena();
|
||||
|
||||
|
@ -25,33 +24,32 @@ public:
|
|||
void DebugFill();
|
||||
|
||||
void* Alloc(size_t size);
|
||||
template<typename T> T* Alloc() {
|
||||
return (T*)Alloc(sizeof(T));
|
||||
template <typename T>
|
||||
T* Alloc() {
|
||||
return reinterpret_cast<T*>(Alloc(sizeof(T)));
|
||||
}
|
||||
|
||||
void* CloneContents();
|
||||
|
||||
private:
|
||||
private:
|
||||
class Chunk {
|
||||
public:
|
||||
public:
|
||||
Chunk(size_t chunk_size);
|
||||
~Chunk();
|
||||
|
||||
Chunk* next;
|
||||
Chunk* next;
|
||||
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t chunk_size_;
|
||||
Chunk* head_chunk_;
|
||||
Chunk* active_chunk_;
|
||||
private:
|
||||
size_t chunk_size_;
|
||||
Chunk* head_chunk_;
|
||||
Chunk* active_chunk_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_ARENA_H_
|
||||
|
|
|
@ -7,26 +7,18 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/assembler.h>
|
||||
#include "alloy/backend/assembler.h"
|
||||
|
||||
#include <alloy/backend/tracing.h>
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::runtime;
|
||||
Assembler::Assembler(Backend* backend) : backend_(backend) {}
|
||||
|
||||
Assembler::~Assembler() { Reset(); }
|
||||
|
||||
Assembler::Assembler(Backend* backend) :
|
||||
backend_(backend) {
|
||||
}
|
||||
int Assembler::Initialize() { return 0; }
|
||||
|
||||
Assembler::~Assembler() {
|
||||
Reset();
|
||||
}
|
||||
void Assembler::Reset() {}
|
||||
|
||||
int Assembler::Initialize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Assembler::Reset() {
|
||||
}
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,29 +10,27 @@
|
|||
#ifndef ALLOY_BACKEND_ASSEMBLER_H_
|
||||
#define ALLOY_BACKEND_ASSEMBLER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace alloy {
|
||||
namespace hir {
|
||||
class HIRBuilder;
|
||||
}
|
||||
} // namespace hir
|
||||
namespace runtime {
|
||||
class DebugInfo;
|
||||
class Function;
|
||||
class FunctionInfo;
|
||||
class Runtime;
|
||||
}
|
||||
}
|
||||
} // namespace runtime
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
class Backend;
|
||||
|
||||
|
||||
class Assembler {
|
||||
public:
|
||||
public:
|
||||
Assembler(Backend* backend);
|
||||
virtual ~Assembler();
|
||||
|
||||
|
@ -40,18 +38,17 @@ public:
|
|||
|
||||
virtual void Reset();
|
||||
|
||||
virtual int Assemble(
|
||||
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
runtime::Function** out_function) = 0;
|
||||
virtual int Assemble(runtime::FunctionInfo* symbol_info,
|
||||
hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||
uint32_t trace_flags,
|
||||
runtime::Function** out_function) = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
Backend* backend_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_ASSEMBLER_H_
|
||||
|
|
|
@ -7,30 +7,24 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include "alloy/backend/backend.h"
|
||||
|
||||
#include <alloy/backend/tracing.h>
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::runtime;
|
||||
using alloy::runtime::Runtime;
|
||||
|
||||
|
||||
Backend::Backend(Runtime* runtime) :
|
||||
runtime_(runtime) {
|
||||
xe_zero_struct(&machine_info_, sizeof(machine_info_));
|
||||
Backend::Backend(Runtime* runtime) : runtime_(runtime) {
|
||||
memset(&machine_info_, 0, sizeof(machine_info_));
|
||||
}
|
||||
|
||||
Backend::~Backend() {
|
||||
}
|
||||
Backend::~Backend() = default;
|
||||
|
||||
int Backend::Initialize() {
|
||||
return 0;
|
||||
}
|
||||
int Backend::Initialize() { return 0; }
|
||||
|
||||
void* Backend::AllocThreadData() {
|
||||
return NULL;
|
||||
}
|
||||
void* Backend::AllocThreadData() { return nullptr; }
|
||||
|
||||
void Backend::FreeThreadData(void* thread_data) {
|
||||
}
|
||||
void Backend::FreeThreadData(void* thread_data) {}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,20 +10,23 @@
|
|||
#ifndef ALLOY_BACKEND_BACKEND_H_
|
||||
#define ALLOY_BACKEND_BACKEND_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include <alloy/backend/machine_info.h>
|
||||
#include <memory>
|
||||
|
||||
#include "alloy/backend/machine_info.h"
|
||||
|
||||
namespace alloy { namespace runtime { class Runtime; } }
|
||||
namespace alloy {
|
||||
namespace runtime {
|
||||
class Runtime;
|
||||
} // namespace runtime
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
class Assembler;
|
||||
|
||||
|
||||
class Backend {
|
||||
public:
|
||||
public:
|
||||
Backend(runtime::Runtime* runtime);
|
||||
virtual ~Backend();
|
||||
|
||||
|
@ -35,16 +38,14 @@ public:
|
|||
virtual void* AllocThreadData();
|
||||
virtual void FreeThreadData(void* thread_data);
|
||||
|
||||
virtual Assembler* CreateAssembler() = 0;
|
||||
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
runtime::Runtime* runtime_;
|
||||
MachineInfo machine_info_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_BACKEND_H_
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <alloy/backend/ivm/ivm_assembler.h>
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include <alloy/backend/ivm/ivm_intcode.h>
|
||||
#include <alloy/backend/ivm/ivm_function.h>
|
||||
#include <alloy/backend/ivm/tracing.h>
|
||||
#include <alloy/hir/hir_builder.h>
|
||||
#include <alloy/hir/label.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::ivm;
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
|
||||
IVMAssembler::IVMAssembler(Backend* backend) :
|
||||
source_map_arena_(128 * 1024),
|
||||
Assembler(backend) {
|
||||
}
|
||||
|
||||
IVMAssembler::~IVMAssembler() {
|
||||
alloy::tracing::WriteEvent(EventType::AssemblerDeinit({
|
||||
}));
|
||||
}
|
||||
|
||||
int IVMAssembler::Initialize() {
|
||||
int result = Assembler::Initialize();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::AssemblerInit({
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void IVMAssembler::Reset() {
|
||||
intcode_arena_.Reset();
|
||||
source_map_arena_.Reset();
|
||||
scratch_arena_.Reset();
|
||||
Assembler::Reset();
|
||||
}
|
||||
|
||||
int IVMAssembler::Assemble(
|
||||
FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
Function** out_function) {
|
||||
IVMFunction* fn = new IVMFunction(symbol_info);
|
||||
fn->set_debug_info(debug_info);
|
||||
|
||||
TranslationContext ctx;
|
||||
ctx.register_count = 0;
|
||||
ctx.intcode_count = 0;
|
||||
ctx.intcode_arena = &intcode_arena_;
|
||||
ctx.source_map_count = 0;
|
||||
ctx.source_map_arena = &source_map_arena_;
|
||||
ctx.scratch_arena = &scratch_arena_;
|
||||
ctx.label_ref_head = NULL;
|
||||
|
||||
// Reset label tags as we use them.
|
||||
builder->ResetLabelTags();
|
||||
|
||||
// Function prologue.
|
||||
size_t stack_offset = 0;
|
||||
auto locals = builder->locals();
|
||||
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 = XEALIGN(stack_offset, type_size);
|
||||
slot->set_constant((uint32_t)stack_offset);
|
||||
stack_offset += type_size;
|
||||
}
|
||||
// Ensure 16b alignment.
|
||||
stack_offset = XEALIGN(stack_offset, 16);
|
||||
ctx.stack_size = stack_offset;
|
||||
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
Label* label = block->label_head;
|
||||
while (label) {
|
||||
label->tag = (void*)(0x80000000 | ctx.intcode_count);
|
||||
label = label->next;
|
||||
}
|
||||
|
||||
Instr* i = block->instr_head;
|
||||
while (i) {
|
||||
int result = TranslateIntCodes(ctx, i);
|
||||
i = i->next;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
// Function epilogue.
|
||||
|
||||
// Fixup label references.
|
||||
LabelRef* label_ref = ctx.label_ref_head;
|
||||
while (label_ref) {
|
||||
label_ref->instr->src1_reg = (uint32_t)(intptr_t)label_ref->label->tag & ~0x80000000;
|
||||
label_ref = label_ref->next;
|
||||
}
|
||||
|
||||
fn->Setup(ctx);
|
||||
|
||||
*out_function = fn;
|
||||
return 0;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_IVM_ASSEMBLER_H_
|
||||
#define ALLOY_BACKEND_IVM_IVM_ASSEMBLER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/backend/assembler.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
|
||||
class IVMAssembler : public Assembler {
|
||||
public:
|
||||
IVMAssembler(Backend* backend);
|
||||
virtual ~IVMAssembler();
|
||||
|
||||
virtual int Initialize();
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual int Assemble(
|
||||
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
runtime::Function** out_function);
|
||||
|
||||
private:
|
||||
Arena intcode_arena_;
|
||||
Arena source_map_arena_;
|
||||
Arena scratch_arena_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_IVM_ASSEMBLER_H_
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <alloy/backend/ivm/ivm_backend.h>
|
||||
|
||||
#include <alloy/backend/ivm/ivm_assembler.h>
|
||||
#include <alloy/backend/ivm/ivm_stack.h>
|
||||
#include <alloy/backend/ivm/tracing.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::ivm;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
|
||||
IVMBackend::IVMBackend(Runtime* runtime) :
|
||||
Backend(runtime) {
|
||||
}
|
||||
|
||||
IVMBackend::~IVMBackend() {
|
||||
alloy::tracing::WriteEvent(EventType::Deinit({
|
||||
}));
|
||||
}
|
||||
|
||||
int IVMBackend::Initialize() {
|
||||
int result = Backend::Initialize();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
machine_info_.register_sets[0] = {
|
||||
0,
|
||||
"gpr",
|
||||
MachineInfo::RegisterSet::INT_TYPES,
|
||||
16,
|
||||
};
|
||||
machine_info_.register_sets[1] = {
|
||||
1,
|
||||
"vec",
|
||||
MachineInfo::RegisterSet::FLOAT_TYPES |
|
||||
MachineInfo::RegisterSet::VEC_TYPES,
|
||||
16,
|
||||
};
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::Init({
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void* IVMBackend::AllocThreadData() {
|
||||
return new IVMStack();
|
||||
}
|
||||
|
||||
void IVMBackend::FreeThreadData(void* thread_data) {
|
||||
auto stack = (IVMStack*)thread_data;
|
||||
delete stack;
|
||||
}
|
||||
|
||||
Assembler* IVMBackend::CreateAssembler() {
|
||||
return new IVMAssembler(this);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_IVM_BACKEND_H_
|
||||
#define ALLOY_BACKEND_IVM_IVM_BACKEND_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
|
||||
#define ALLOY_HAS_IVM_BACKEND 1
|
||||
|
||||
|
||||
class IVMBackend : public Backend {
|
||||
public:
|
||||
IVMBackend(runtime::Runtime* runtime);
|
||||
virtual ~IVMBackend();
|
||||
|
||||
virtual int Initialize();
|
||||
|
||||
virtual void* AllocThreadData();
|
||||
virtual void FreeThreadData(void* thread_data);
|
||||
|
||||
virtual Assembler* CreateAssembler();
|
||||
};
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_IVM_BACKEND_H_
|
|
@ -1,179 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <alloy/backend/ivm/ivm_function.h>
|
||||
|
||||
#include <alloy/backend/ivm/ivm_stack.h>
|
||||
#include <alloy/backend/tracing.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::ivm;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
|
||||
IVMFunction::IVMFunction(FunctionInfo* symbol_info) :
|
||||
register_count_(0), intcode_count_(0), intcodes_(0),
|
||||
source_map_count_(0), source_map_(0),
|
||||
Function(symbol_info) {
|
||||
}
|
||||
|
||||
IVMFunction::~IVMFunction() {
|
||||
xe_free(intcodes_);
|
||||
xe_free(source_map_);
|
||||
}
|
||||
|
||||
void IVMFunction::Setup(TranslationContext& ctx) {
|
||||
register_count_ = ctx.register_count;
|
||||
stack_size_ = ctx.stack_size;
|
||||
intcode_count_ = ctx.intcode_count;
|
||||
intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents();
|
||||
source_map_count_ = ctx.source_map_count;
|
||||
source_map_ = (SourceMapEntry*)ctx.source_map_arena->CloneContents();
|
||||
}
|
||||
|
||||
IntCode* IVMFunction::GetIntCodeAtSourceOffset(uint64_t offset) {
|
||||
for (size_t n = 0; n < source_map_count_; n++) {
|
||||
auto entry = &source_map_[n];
|
||||
if (entry->source_offset == offset) {
|
||||
return &intcodes_[entry->intcode_index];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int IVMFunction::AddBreakpointImpl(Breakpoint* breakpoint) {
|
||||
auto i = GetIntCodeAtSourceOffset(breakpoint->address());
|
||||
if (!i) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TEMP breakpoints always overwrite normal ones.
|
||||
if (!i->debug_flags ||
|
||||
breakpoint->type() == Breakpoint::TEMP_TYPE) {
|
||||
uint64_t breakpoint_ptr = (uint64_t)breakpoint;
|
||||
i->src2_reg = (uint32_t)breakpoint_ptr;
|
||||
i->src3_reg = (uint32_t)(breakpoint_ptr >> 32);
|
||||
}
|
||||
|
||||
// Increment breakpoint counter.
|
||||
++i->debug_flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IVMFunction::RemoveBreakpointImpl(Breakpoint* breakpoint) {
|
||||
auto i = GetIntCodeAtSourceOffset(breakpoint->address());
|
||||
if (!i) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decrement breakpoint counter.
|
||||
--i->debug_flags;
|
||||
i->src2_reg = i->src3_reg = 0;
|
||||
|
||||
// If there were other breakpoints, see what they were.
|
||||
if (i->debug_flags) {
|
||||
auto old_breakpoint = FindBreakpoint(breakpoint->address());
|
||||
if (old_breakpoint) {
|
||||
uint64_t breakpoint_ptr = (uint64_t)old_breakpoint;
|
||||
i->src2_reg = (uint32_t)breakpoint_ptr;
|
||||
i->src3_reg = (uint32_t)(breakpoint_ptr >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IVMFunction::OnBreakpointHit(ThreadState* thread_state, IntCode* i) {
|
||||
uint64_t breakpoint_ptr = i->src2_reg | (uint64_t(i->src3_reg) << 32);
|
||||
Breakpoint* breakpoint = (Breakpoint*)breakpoint_ptr;
|
||||
|
||||
// Notify debugger.
|
||||
// The debugger may choose to wait (blocking us).
|
||||
auto debugger = thread_state->runtime()->debugger();
|
||||
debugger->OnBreakpointHit(thread_state, breakpoint);
|
||||
}
|
||||
|
||||
#undef TRACE_SOURCE_OFFSET
|
||||
|
||||
int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
||||
// Setup register file on stack.
|
||||
auto stack = (IVMStack*)thread_state->backend_data();
|
||||
auto register_file = (Register*)stack->Alloc(register_count_);
|
||||
auto local_stack = (uint8_t*)alloca(stack_size_);
|
||||
|
||||
Memory* memory = thread_state->memory();
|
||||
|
||||
IntCodeState ics;
|
||||
ics.rf = register_file;
|
||||
ics.locals = local_stack;
|
||||
ics.context = (uint8_t*)thread_state->raw_context();
|
||||
ics.membase = memory->membase();
|
||||
ics.page_table = ics.membase + memory->page_table();
|
||||
ics.did_carry = 0;
|
||||
ics.did_saturate = 0;
|
||||
ics.thread_state = thread_state;
|
||||
ics.return_address = return_address;
|
||||
ics.call_return_address = 0;
|
||||
|
||||
volatile int* suspend_flag_address = thread_state->suspend_flag_address();
|
||||
|
||||
// TODO(benvanik): DID_CARRY -- need HIR to set a OPCODE_FLAG_SET_CARRY
|
||||
// or something so the fns can set an ics flag.
|
||||
|
||||
#ifdef TRACE_SOURCE_OFFSET
|
||||
size_t source_index = 0;
|
||||
#endif
|
||||
|
||||
uint32_t ia = 0;
|
||||
while (true) {
|
||||
// Check suspend. We could do this only on certain instructions, if we
|
||||
// wanted to speed things up.
|
||||
if (*suspend_flag_address) {
|
||||
thread_state->EnterSuspend();
|
||||
}
|
||||
|
||||
#ifdef TRACE_SOURCE_OFFSET
|
||||
uint64_t source_offset = -1;
|
||||
if (source_index < this->source_map_count_ &&
|
||||
this->source_map_[source_index].intcode_index <= ia) {
|
||||
while (source_index + 1 < this->source_map_count_ &&
|
||||
this->source_map_[source_index + 1].intcode_index <= ia) {
|
||||
source_index++;
|
||||
}
|
||||
source_offset = this->source_map_[source_index].source_offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
IntCode* i = &intcodes_[ia];
|
||||
|
||||
if (i->debug_flags) {
|
||||
OnBreakpointHit(thread_state, i);
|
||||
}
|
||||
|
||||
uint32_t new_ia = i->intcode_fn(ics, i);
|
||||
if (new_ia == IA_NEXT) {
|
||||
ia++;
|
||||
} else if (new_ia == IA_RETURN) {
|
||||
break;
|
||||
} else {
|
||||
ia = new_ia;
|
||||
#ifdef TRACE_SOURCE_OFFSET
|
||||
source_index = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
stack->Free(register_count_);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_IVM_FUNCTION_H_
|
||||
#define ALLOY_BACKEND_IVM_IVM_FUNCTION_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include <alloy/backend/ivm/ivm_intcode.h>
|
||||
#include <alloy/runtime/function.h>
|
||||
#include <alloy/runtime/symbol_info.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
|
||||
class IVMFunction : public runtime::Function {
|
||||
public:
|
||||
IVMFunction(runtime::FunctionInfo* symbol_info);
|
||||
virtual ~IVMFunction();
|
||||
|
||||
void Setup(TranslationContext& ctx);
|
||||
|
||||
protected:
|
||||
virtual int AddBreakpointImpl(runtime::Breakpoint* breakpoint);
|
||||
virtual int RemoveBreakpointImpl(runtime::Breakpoint* breakpoint);
|
||||
virtual int CallImpl(runtime::ThreadState* thread_state,
|
||||
uint64_t return_address);
|
||||
|
||||
private:
|
||||
IntCode* GetIntCodeAtSourceOffset(uint64_t offset);
|
||||
void OnBreakpointHit(runtime::ThreadState* thread_state, IntCode* i);
|
||||
|
||||
private:
|
||||
size_t register_count_;
|
||||
size_t stack_size_;
|
||||
size_t intcode_count_;
|
||||
IntCode* intcodes_;
|
||||
size_t source_map_count_;
|
||||
SourceMapEntry* source_map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_IVM_FUNCTION_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -1,118 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_INTCODE_H_
|
||||
#define ALLOY_BACKEND_IVM_INTCODE_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/hir/instr.h>
|
||||
#include <alloy/hir/opcodes.h>
|
||||
|
||||
namespace alloy { namespace runtime { class ThreadState; } }
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
|
||||
typedef union {
|
||||
int8_t i8;
|
||||
uint8_t u8;
|
||||
int16_t i16;
|
||||
uint16_t u16;
|
||||
int32_t i32;
|
||||
uint32_t u32;
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
float f32;
|
||||
double f64;
|
||||
vec128_t v128;
|
||||
} Register;
|
||||
|
||||
|
||||
typedef struct {
|
||||
Register* rf;
|
||||
uint8_t* locals;
|
||||
uint8_t* context;
|
||||
uint8_t* membase;
|
||||
uint8_t* page_table;
|
||||
int8_t did_carry;
|
||||
int8_t did_saturate;
|
||||
runtime::ThreadState* thread_state;
|
||||
uint64_t return_address;
|
||||
uint64_t call_return_address;
|
||||
} IntCodeState;
|
||||
|
||||
|
||||
struct IntCode_s;
|
||||
typedef uint32_t (*IntCodeFn)(
|
||||
IntCodeState& ics, const struct IntCode_s* i);
|
||||
|
||||
#define IA_RETURN 0xA0000000
|
||||
#define IA_NEXT 0xB0000000
|
||||
|
||||
|
||||
typedef struct IntCode_s {
|
||||
IntCodeFn intcode_fn;
|
||||
uint16_t flags;
|
||||
uint16_t debug_flags;
|
||||
|
||||
uint32_t dest_reg;
|
||||
union {
|
||||
struct {
|
||||
uint32_t src1_reg;
|
||||
uint32_t src2_reg;
|
||||
uint32_t src3_reg;
|
||||
// <4 bytes available>
|
||||
};
|
||||
struct {
|
||||
Register constant;
|
||||
};
|
||||
};
|
||||
|
||||
// debugging info/etc
|
||||
} IntCode;
|
||||
|
||||
|
||||
typedef struct LabelRef_s {
|
||||
hir::Label* label;
|
||||
IntCode* instr;
|
||||
LabelRef_s* next;
|
||||
} LabelRef;
|
||||
|
||||
|
||||
typedef struct SourceMapEntry_s {
|
||||
uint64_t source_offset;
|
||||
uint64_t intcode_index;
|
||||
} SourceMapEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t register_count;
|
||||
size_t intcode_count;
|
||||
Arena* intcode_arena;
|
||||
size_t source_map_count;
|
||||
Arena* source_map_arena;
|
||||
Arena* scratch_arena;
|
||||
LabelRef* label_ref_head;
|
||||
size_t stack_size;
|
||||
} TranslationContext;
|
||||
|
||||
|
||||
int TranslateIntCodes(TranslationContext& ctx, hir::Instr* i);
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_INTCODE_H_
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <alloy/backend/ivm/ivm_stack.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::ivm;
|
||||
|
||||
|
||||
IVMStack::IVMStack() :
|
||||
chunk_size_(2 * 1024 * 1024),
|
||||
head_chunk_(NULL), active_chunk_(NULL) {
|
||||
}
|
||||
|
||||
IVMStack::~IVMStack() {
|
||||
Chunk* chunk = head_chunk_;
|
||||
while (chunk) {
|
||||
Chunk* next = chunk->next;
|
||||
delete chunk;
|
||||
chunk = next;
|
||||
}
|
||||
head_chunk_ = NULL;
|
||||
}
|
||||
|
||||
Register* IVMStack::Alloc(size_t register_count) {
|
||||
size_t size = register_count * sizeof(Register);
|
||||
if (active_chunk_) {
|
||||
if (active_chunk_->capacity - active_chunk_->offset < size) {
|
||||
Chunk* next = active_chunk_->next;
|
||||
if (!next) {
|
||||
XEASSERT(size < chunk_size_); // need to support larger chunks
|
||||
next = new Chunk(chunk_size_);
|
||||
next->prev = active_chunk_;
|
||||
active_chunk_->next = next;
|
||||
}
|
||||
next->offset = 0;
|
||||
active_chunk_ = next;
|
||||
}
|
||||
} else {
|
||||
head_chunk_ = active_chunk_ = new Chunk(chunk_size_);
|
||||
}
|
||||
|
||||
uint8_t* p = active_chunk_->buffer + active_chunk_->offset;
|
||||
active_chunk_->offset += size;
|
||||
return (Register*)p;
|
||||
}
|
||||
|
||||
void IVMStack::Free(size_t register_count) {
|
||||
size_t size = register_count * sizeof(Register);
|
||||
if (active_chunk_->offset == size) {
|
||||
// Moving back a chunk.
|
||||
active_chunk_->offset = 0;
|
||||
if (active_chunk_->prev) {
|
||||
active_chunk_ = active_chunk_->prev;
|
||||
}
|
||||
} else {
|
||||
// Still in same chunk.
|
||||
active_chunk_->offset -= size;
|
||||
}
|
||||
}
|
||||
|
||||
IVMStack::Chunk::Chunk(size_t chunk_size) :
|
||||
prev(NULL), next(NULL),
|
||||
capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = (uint8_t*)xe_malloc(capacity);
|
||||
}
|
||||
|
||||
IVMStack::Chunk::~Chunk() {
|
||||
if (buffer) {
|
||||
xe_free(buffer);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_IVM_STACK_H_
|
||||
#define ALLOY_BACKEND_IVM_IVM_STACK_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/backend/ivm/ivm_intcode.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
|
||||
class IVMStack {
|
||||
public:
|
||||
IVMStack();
|
||||
~IVMStack();
|
||||
|
||||
Register* Alloc(size_t register_count);
|
||||
void Free(size_t register_count);
|
||||
|
||||
private:
|
||||
class Chunk {
|
||||
public:
|
||||
Chunk(size_t chunk_size);
|
||||
~Chunk();
|
||||
|
||||
Chunk* prev;
|
||||
Chunk* next;
|
||||
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t chunk_size_;
|
||||
Chunk* head_chunk_;
|
||||
Chunk* active_chunk_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_IVM_STACK_H_
|
|
@ -1,15 +0,0 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'ivm_intcode.cc',
|
||||
'ivm_intcode.h',
|
||||
'ivm_assembler.cc',
|
||||
'ivm_assembler.h',
|
||||
'ivm_backend.cc',
|
||||
'ivm_backend.h',
|
||||
'ivm_function.cc',
|
||||
'ivm_stack.cc',
|
||||
'ivm_stack.h',
|
||||
'tracing.h',
|
||||
],
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_IVM_TRACING_H_
|
||||
#define ALLOY_BACKEND_IVM_TRACING_H_
|
||||
|
||||
#include <alloy/backend/tracing.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace ivm {
|
||||
|
||||
const uint32_t ALLOY_BACKEND_IVM =
|
||||
alloy::backend::EventType::ALLOY_BACKEND_IVM;
|
||||
|
||||
|
||||
class EventType {
|
||||
public:
|
||||
enum {
|
||||
ALLOY_BACKEND_IVM_INIT = ALLOY_BACKEND_IVM | (1),
|
||||
ALLOY_BACKEND_IVM_DEINIT = ALLOY_BACKEND_IVM | (2),
|
||||
|
||||
ALLOY_BACKEND_IVM_ASSEMBLER = ALLOY_BACKEND_IVM | (1 << 20),
|
||||
ALLOY_BACKEND_IVM_ASSEMBLER_INIT = ALLOY_BACKEND_IVM_ASSEMBLER | (1),
|
||||
ALLOY_BACKEND_IVM_ASSEMBLER_DEINIT = ALLOY_BACKEND_IVM_ASSEMBLER | (2),
|
||||
};
|
||||
|
||||
typedef struct Init_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_IVM_INIT;
|
||||
} Init;
|
||||
typedef struct Deinit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_IVM_DEINIT;
|
||||
} Deinit;
|
||||
|
||||
typedef struct AssemblerInit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_IVM_ASSEMBLER_INIT;
|
||||
} AssemblerInit;
|
||||
typedef struct AssemblerDeinit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_IVM_ASSEMBLER_DEINIT;
|
||||
} AssemblerDeinit;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ivm
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_IVM_TRACING_H_
|
|
@ -10,13 +10,11 @@
|
|||
#ifndef ALLOY_BACKEND_MACHINE_INFO_H_
|
||||
#define ALLOY_BACKEND_MACHINE_INFO_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
|
||||
struct MachineInfo {
|
||||
struct RegisterSet {
|
||||
enum Types {
|
||||
|
@ -31,9 +29,7 @@ struct MachineInfo {
|
|||
} register_sets[8];
|
||||
};
|
||||
|
||||
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_MACHINE_INFO_H_
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
'backend.cc',
|
||||
'backend.h',
|
||||
'machine_info.h',
|
||||
'tracing.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'ivm/sources.gypi',
|
||||
'x64/sources.gypi',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_TRACING_H_
|
||||
#define ALLOY_BACKEND_TRACING_H_
|
||||
|
||||
#include <alloy/tracing/tracing.h>
|
||||
#include <alloy/tracing/event_type.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
||||
const uint32_t ALLOY_BACKEND = alloy::tracing::EventType::ALLOY_BACKEND;
|
||||
|
||||
|
||||
class EventType {
|
||||
public:
|
||||
enum {
|
||||
ALLOY_BACKEND_IVM = ALLOY_BACKEND | (1 << 24),
|
||||
ALLOY_BACKEND_X64 = ALLOY_BACKEND | (2 << 24),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_TRACING_H_
|
|
@ -1,12 +1,10 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'tracing.h',
|
||||
'x64_assembler.cc',
|
||||
'x64_assembler.h',
|
||||
'x64_backend.cc',
|
||||
'x64_backend.h',
|
||||
'x64_code_cache.cc',
|
||||
'x64_code_cache.h',
|
||||
'x64_emitter.cc',
|
||||
'x64_emitter.h',
|
||||
|
@ -20,4 +18,17 @@
|
|||
'x64_tracers.cc',
|
||||
'x64_tracers.h',
|
||||
],
|
||||
|
||||
'conditions': [
|
||||
['OS == "mac" or OS == "linux"', {
|
||||
'sources': [
|
||||
'x64_code_cache_posix.cc',
|
||||
],
|
||||
}],
|
||||
['OS == "win"', {
|
||||
'sources': [
|
||||
'x64_code_cache_win.cc',
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 ALLOY_BACKEND_X64_TRACING_H_
|
||||
#define ALLOY_BACKEND_X64_TRACING_H_
|
||||
|
||||
#include <alloy/backend/tracing.h>
|
||||
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
const uint32_t ALLOY_BACKEND_X64 =
|
||||
alloy::backend::EventType::ALLOY_BACKEND_X64;
|
||||
|
||||
|
||||
class EventType {
|
||||
public:
|
||||
enum {
|
||||
ALLOY_BACKEND_X64_INIT = ALLOY_BACKEND_X64 | (1),
|
||||
ALLOY_BACKEND_X64_DEINIT = ALLOY_BACKEND_X64 | (2),
|
||||
|
||||
ALLOY_BACKEND_X64_ASSEMBLER = ALLOY_BACKEND_X64 | (1 << 20),
|
||||
ALLOY_BACKEND_X64_ASSEMBLER_INIT = ALLOY_BACKEND_X64_ASSEMBLER | (1),
|
||||
ALLOY_BACKEND_X64_ASSEMBLER_DEINIT = ALLOY_BACKEND_X64_ASSEMBLER | (2),
|
||||
};
|
||||
|
||||
typedef struct Init_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_X64_INIT;
|
||||
} Init;
|
||||
typedef struct Deinit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_X64_DEINIT;
|
||||
} Deinit;
|
||||
|
||||
typedef struct AssemblerInit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_X64_ASSEMBLER_INIT;
|
||||
} AssemblerInit;
|
||||
typedef struct AssemblerDeinit_s {
|
||||
static const uint32_t event_type = ALLOY_BACKEND_X64_ASSEMBLER_DEINIT;
|
||||
} AssemblerDeinit;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_TRACING_H_
|
|
@ -7,39 +7,40 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_assembler.h>
|
||||
#include "alloy/backend/x64/x64_assembler.h"
|
||||
|
||||
#include <alloy/backend/x64/tracing.h>
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include <alloy/backend/x64/x64_emitter.h>
|
||||
#include <alloy/backend/x64/x64_function.h>
|
||||
#include <alloy/hir/hir_builder.h>
|
||||
#include <alloy/hir/label.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include "alloy/reset_scope.h"
|
||||
#include "alloy/backend/x64/x64_backend.h"
|
||||
#include "alloy/backend/x64/x64_emitter.h"
|
||||
#include "alloy/backend/x64/x64_function.h"
|
||||
#include "alloy/hir/hir_builder.h"
|
||||
#include "alloy/hir/label.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace BE {
|
||||
#include <beaengine/BeaEngine.h>
|
||||
}
|
||||
} // namespace BE
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
using namespace alloy::hir;
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::runtime;
|
||||
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::runtime::DebugInfo;
|
||||
using alloy::runtime::Function;
|
||||
using alloy::runtime::FunctionInfo;
|
||||
|
||||
X64Assembler::X64Assembler(X64Backend* backend) :
|
||||
x64_backend_(backend),
|
||||
emitter_(0), allocator_(0),
|
||||
Assembler(backend) {
|
||||
}
|
||||
X64Assembler::X64Assembler(X64Backend* backend)
|
||||
: Assembler(backend), x64_backend_(backend) {}
|
||||
|
||||
X64Assembler::~X64Assembler() {
|
||||
alloy::tracing::WriteEvent(EventType::AssemblerDeinit({
|
||||
}));
|
||||
|
||||
delete emitter_;
|
||||
delete allocator_;
|
||||
// Emitter must be freed before the allocator.
|
||||
emitter_.reset();
|
||||
allocator_.reset();
|
||||
}
|
||||
|
||||
int X64Assembler::Initialize() {
|
||||
|
@ -48,11 +49,8 @@ int X64Assembler::Initialize() {
|
|||
return result;
|
||||
}
|
||||
|
||||
allocator_ = new XbyakAllocator();
|
||||
emitter_ = new X64Emitter(x64_backend_, allocator_);
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::AssemblerInit({
|
||||
}));
|
||||
allocator_.reset(new XbyakAllocator());
|
||||
emitter_.reset(new X64Emitter(x64_backend_, allocator_.get()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -62,50 +60,45 @@ void X64Assembler::Reset() {
|
|||
Assembler::Reset();
|
||||
}
|
||||
|
||||
int X64Assembler::Assemble(
|
||||
FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
||||
Function** out_function) {
|
||||
int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<DebugInfo> debug_info,
|
||||
uint32_t trace_flags, Function** out_function) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
int result = 0;
|
||||
// Reset when we leave.
|
||||
make_reset_scope(this);
|
||||
|
||||
// Lower HIR -> x64.
|
||||
void* machine_code = 0;
|
||||
size_t code_size = 0;
|
||||
result = emitter_->Emit(builder,
|
||||
debug_info_flags, debug_info,
|
||||
machine_code, code_size);
|
||||
XEEXPECTZERO(result);
|
||||
int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(),
|
||||
trace_flags, machine_code, code_size);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Stash generated machine code.
|
||||
if (debug_info_flags & DEBUG_INFO_MACHINE_CODE_DISASM) {
|
||||
DumpMachineCode(debug_info, machine_code, code_size, &string_buffer_);
|
||||
if (debug_info_flags & DebugInfoFlags::DEBUG_INFO_MACHINE_CODE_DISASM) {
|
||||
DumpMachineCode(debug_info.get(), machine_code, code_size, &string_buffer_);
|
||||
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
{
|
||||
X64Function* fn = new X64Function(symbol_info);
|
||||
fn->set_debug_info(debug_info);
|
||||
fn->set_debug_info(std::move(debug_info));
|
||||
fn->Setup(machine_code, code_size);
|
||||
|
||||
*out_function = fn;
|
||||
|
||||
result = 0;
|
||||
}
|
||||
|
||||
XECLEANUP:
|
||||
Reset();
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void X64Assembler::DumpMachineCode(
|
||||
DebugInfo* debug_info,
|
||||
void* machine_code, size_t code_size,
|
||||
StringBuffer* str) {
|
||||
BE::DISASM disasm;
|
||||
xe_zero_struct(&disasm, sizeof(disasm));
|
||||
void X64Assembler::DumpMachineCode(DebugInfo* debug_info, void* machine_code,
|
||||
size_t code_size, StringBuffer* str) {
|
||||
BE::DISASM disasm = {0};
|
||||
disasm.Archi = 64;
|
||||
disasm.Options = BE::Tabulation + BE::MasmSyntax + BE::PrefixedNumeral;
|
||||
disasm.EIP = (BE::UIntPtr)machine_code;
|
||||
|
@ -113,8 +106,8 @@ void X64Assembler::DumpMachineCode(
|
|||
uint64_t prev_source_offset = 0;
|
||||
while (disasm.EIP < eip_end) {
|
||||
// Look up source offset.
|
||||
auto map_entry = debug_info->LookupCodeOffset(
|
||||
disasm.EIP - (BE::UIntPtr)machine_code);
|
||||
auto map_entry =
|
||||
debug_info->LookupCodeOffset(disasm.EIP - (BE::UIntPtr)machine_code);
|
||||
if (map_entry) {
|
||||
if (map_entry->source_offset == prev_source_offset) {
|
||||
str->Append(" ");
|
||||
|
@ -134,3 +127,7 @@ void X64Assembler::DumpMachineCode(
|
|||
disasm.EIP += len;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
||||
#define ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/backend/assembler.h>
|
||||
#include <memory>
|
||||
|
||||
#include "alloy/backend/assembler.h"
|
||||
#include "alloy/string_buffer.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -23,38 +23,34 @@ class X64Backend;
|
|||
class X64Emitter;
|
||||
class XbyakAllocator;
|
||||
|
||||
|
||||
class X64Assembler : public Assembler {
|
||||
public:
|
||||
public:
|
||||
X64Assembler(X64Backend* backend);
|
||||
virtual ~X64Assembler();
|
||||
~X64Assembler() override;
|
||||
|
||||
virtual int Initialize();
|
||||
int Initialize() override;
|
||||
|
||||
virtual void Reset();
|
||||
void Reset() override;
|
||||
|
||||
virtual int Assemble(
|
||||
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
runtime::Function** out_function);
|
||||
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||
uint32_t trace_flags, runtime::Function** out_function) override;
|
||||
|
||||
private:
|
||||
void DumpMachineCode(runtime::DebugInfo* debug_info,
|
||||
void* machine_code, size_t code_size,
|
||||
StringBuffer* str);
|
||||
private:
|
||||
void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code,
|
||||
size_t code_size, StringBuffer* str);
|
||||
|
||||
private:
|
||||
X64Backend* x64_backend_;
|
||||
X64Emitter* emitter_;
|
||||
XbyakAllocator* allocator_;
|
||||
private:
|
||||
X64Backend* x64_backend_;
|
||||
std::unique_ptr<X64Emitter> emitter_;
|
||||
std::unique_ptr<XbyakAllocator> allocator_;
|
||||
|
||||
StringBuffer string_buffer_;
|
||||
StringBuffer string_buffer_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
||||
|
|
|
@ -7,30 +7,22 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include "alloy/backend/x64/x64_backend.h"
|
||||
|
||||
#include <alloy/backend/x64/tracing.h>
|
||||
#include <alloy/backend/x64/x64_assembler.h>
|
||||
#include <alloy/backend/x64/x64_code_cache.h>
|
||||
#include <alloy/backend/x64/x64_sequences.h>
|
||||
#include <alloy/backend/x64/x64_thunk_emitter.h>
|
||||
#include "alloy/backend/x64/x64_assembler.h"
|
||||
#include "alloy/backend/x64/x64_code_cache.h"
|
||||
#include "alloy/backend/x64/x64_sequences.h"
|
||||
#include "alloy/backend/x64/x64_thunk_emitter.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
using namespace alloy::runtime;
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
using alloy::runtime::Runtime;
|
||||
|
||||
X64Backend::X64Backend(Runtime* runtime) :
|
||||
code_cache_(0),
|
||||
Backend(runtime) {
|
||||
}
|
||||
X64Backend::X64Backend(Runtime* runtime) : Backend(runtime), code_cache_(0) {}
|
||||
|
||||
X64Backend::~X64Backend() {
|
||||
alloy::tracing::WriteEvent(EventType::Deinit({
|
||||
}));
|
||||
delete code_cache_;
|
||||
}
|
||||
X64Backend::~X64Backend() { delete code_cache_; }
|
||||
|
||||
int X64Backend::Initialize() {
|
||||
int result = Backend::Initialize();
|
||||
|
@ -41,17 +33,12 @@ int X64Backend::Initialize() {
|
|||
RegisterSequences();
|
||||
|
||||
machine_info_.register_sets[0] = {
|
||||
0,
|
||||
"gpr",
|
||||
MachineInfo::RegisterSet::INT_TYPES,
|
||||
X64Emitter::GPR_COUNT,
|
||||
0, "gpr", MachineInfo::RegisterSet::INT_TYPES, X64Emitter::GPR_COUNT,
|
||||
};
|
||||
machine_info_.register_sets[1] = {
|
||||
1,
|
||||
"xmm",
|
||||
MachineInfo::RegisterSet::FLOAT_TYPES |
|
||||
MachineInfo::RegisterSet::VEC_TYPES,
|
||||
X64Emitter::XMM_COUNT,
|
||||
1, "xmm", MachineInfo::RegisterSet::FLOAT_TYPES |
|
||||
MachineInfo::RegisterSet::VEC_TYPES,
|
||||
X64Emitter::XMM_COUNT,
|
||||
};
|
||||
|
||||
code_cache_ = new X64CodeCache();
|
||||
|
@ -60,19 +47,19 @@ int X64Backend::Initialize() {
|
|||
return result;
|
||||
}
|
||||
|
||||
auto allocator = new XbyakAllocator();
|
||||
auto thunk_emitter = new X64ThunkEmitter(this, allocator);
|
||||
// Generate thunks used to transition between jitted code and host code.
|
||||
auto allocator = std::make_unique<XbyakAllocator>();
|
||||
auto thunk_emitter = std::make_unique<X64ThunkEmitter>(this, allocator.get());
|
||||
host_to_guest_thunk_ = thunk_emitter->EmitHostToGuestThunk();
|
||||
guest_to_host_thunk_ = thunk_emitter->EmitGuestToHostThunk();
|
||||
delete thunk_emitter;
|
||||
delete allocator;
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::Init({
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Assembler* X64Backend::CreateAssembler() {
|
||||
return new X64Assembler(this);
|
||||
std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
|
||||
return std::make_unique<X64Assembler>(this);
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,10 +10,7 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_BACKEND_H_
|
||||
#define ALLOY_BACKEND_X64_X64_BACKEND_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
|
||||
#include "alloy/backend/backend.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -21,36 +18,32 @@ namespace x64 {
|
|||
|
||||
class X64CodeCache;
|
||||
|
||||
|
||||
#define ALLOY_HAS_X64_BACKEND 1
|
||||
|
||||
|
||||
typedef void* (*HostToGuestThunk)(void* target, void* arg0, void* arg1);
|
||||
typedef void* (*GuestToHostThunk)(void* target, void* arg0, void* arg1);
|
||||
|
||||
class X64Backend : public Backend {
|
||||
public:
|
||||
public:
|
||||
X64Backend(runtime::Runtime* runtime);
|
||||
virtual ~X64Backend();
|
||||
~X64Backend() override;
|
||||
|
||||
X64CodeCache* code_cache() const { return code_cache_; }
|
||||
HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; }
|
||||
GuestToHostThunk guest_to_host_thunk() const { return guest_to_host_thunk_; }
|
||||
|
||||
virtual int Initialize();
|
||||
int Initialize() override;
|
||||
|
||||
virtual Assembler* CreateAssembler();
|
||||
std::unique_ptr<Assembler> CreateAssembler() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
X64CodeCache* code_cache_;
|
||||
HostToGuestThunk host_to_guest_thunk_;
|
||||
GuestToHostThunk guest_to_host_thunk_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_BACKEND_H_
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
||||
#define ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -20,7 +19,7 @@ namespace x64 {
|
|||
class X64CodeChunk;
|
||||
|
||||
class X64CodeCache {
|
||||
public:
|
||||
public:
|
||||
X64CodeCache(size_t chunk_size = DEFAULT_CHUNK_SIZE);
|
||||
virtual ~X64CodeCache();
|
||||
|
||||
|
@ -32,18 +31,16 @@ public:
|
|||
|
||||
void* PlaceCode(void* machine_code, size_t code_size, size_t stack_size);
|
||||
|
||||
private:
|
||||
private:
|
||||
const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024;
|
||||
Mutex* lock_;
|
||||
size_t chunk_size_;
|
||||
std::mutex lock_;
|
||||
size_t chunk_size_;
|
||||
X64CodeChunk* head_chunk_;
|
||||
X64CodeChunk* active_chunk_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_code_cache.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <poly/assert.h>
|
||||
#include <poly/math.h>
|
||||
#include <xenia/profiling.h>
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
class X64CodeChunk {
|
||||
public:
|
||||
X64CodeChunk(size_t chunk_size);
|
||||
~X64CodeChunk();
|
||||
|
||||
public:
|
||||
X64CodeChunk* next;
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
X64CodeCache::X64CodeCache(size_t chunk_size)
|
||||
: chunk_size_(chunk_size), head_chunk_(NULL), active_chunk_(NULL) {}
|
||||
|
||||
X64CodeCache::~X64CodeCache() {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
auto chunk = head_chunk_;
|
||||
while (chunk) {
|
||||
auto next = chunk->next;
|
||||
delete chunk;
|
||||
chunk = next;
|
||||
}
|
||||
head_chunk_ = NULL;
|
||||
}
|
||||
|
||||
int X64CodeCache::Initialize() { return 0; }
|
||||
|
||||
void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
|
||||
size_t stack_size) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
// Always move the code to land on 16b alignment. We do this by rounding up
|
||||
// to 16b so that all offsets are aligned.
|
||||
code_size = poly::round_up(code_size, 16);
|
||||
|
||||
lock_.lock();
|
||||
|
||||
if (active_chunk_) {
|
||||
if (active_chunk_->capacity - active_chunk_->offset < code_size) {
|
||||
auto next = active_chunk_->next;
|
||||
if (!next) {
|
||||
assert_true(code_size < chunk_size_, "need to support larger chunks");
|
||||
next = new X64CodeChunk(chunk_size_);
|
||||
active_chunk_->next = next;
|
||||
}
|
||||
active_chunk_ = next;
|
||||
}
|
||||
} else {
|
||||
head_chunk_ = active_chunk_ = new X64CodeChunk(chunk_size_);
|
||||
}
|
||||
|
||||
uint8_t* final_address = active_chunk_->buffer + active_chunk_->offset;
|
||||
active_chunk_->offset += code_size;
|
||||
|
||||
lock_.unlock();
|
||||
|
||||
// Copy code.
|
||||
memcpy(final_address, machine_code, code_size);
|
||||
|
||||
return final_address;
|
||||
}
|
||||
|
||||
X64CodeChunk::X64CodeChunk(size_t chunk_size)
|
||||
: next(NULL), capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = (uint8_t*)mmap(nullptr, chunk_size, PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
}
|
||||
|
||||
X64CodeChunk::~X64CodeChunk() {
|
||||
if (buffer) {
|
||||
munmap(buffer, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
|
@ -7,28 +7,25 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_code_cache.h>
|
||||
|
||||
#include <alloy/backend/x64/tracing.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
#include "alloy/backend/x64/x64_code_cache.h"
|
||||
|
||||
#include "poly/poly.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
class X64CodeChunk {
|
||||
public:
|
||||
public:
|
||||
X64CodeChunk(size_t chunk_size);
|
||||
~X64CodeChunk();
|
||||
public:
|
||||
|
||||
public:
|
||||
X64CodeChunk* next;
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
size_t capacity;
|
||||
uint8_t* buffer;
|
||||
size_t offset;
|
||||
|
||||
// Estimate of function sized use to determine initial table capacity.
|
||||
const static uint32_t ESTIMATED_FN_SIZE = 512;
|
||||
|
@ -36,28 +33,19 @@ public:
|
|||
// TODO(benvanik): move this to emitter.
|
||||
const static uint32_t UNWIND_INFO_SIZE = 4 + (2 * 1 + 2 + 2);
|
||||
|
||||
void* fn_table_handle;
|
||||
void* fn_table_handle;
|
||||
RUNTIME_FUNCTION* fn_table;
|
||||
uint32_t fn_table_count;
|
||||
uint32_t fn_table_capacity;
|
||||
uint32_t fn_table_count;
|
||||
uint32_t fn_table_capacity;
|
||||
|
||||
void AddTableEntry(uint8_t* code, size_t code_size, size_t stack_size);
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
X64CodeCache::X64CodeCache(size_t chunk_size) :
|
||||
chunk_size_(chunk_size),
|
||||
head_chunk_(NULL), active_chunk_(NULL) {
|
||||
lock_ = AllocMutex();
|
||||
}
|
||||
X64CodeCache::X64CodeCache(size_t chunk_size)
|
||||
: chunk_size_(chunk_size), head_chunk_(NULL), active_chunk_(NULL) {}
|
||||
|
||||
X64CodeCache::~X64CodeCache() {
|
||||
LockMutex(lock_);
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
auto chunk = head_chunk_;
|
||||
while (chunk) {
|
||||
auto next = chunk->next;
|
||||
|
@ -65,32 +53,30 @@ X64CodeCache::~X64CodeCache() {
|
|||
chunk = next;
|
||||
}
|
||||
head_chunk_ = NULL;
|
||||
UnlockMutex(lock_);
|
||||
FreeMutex(lock_);
|
||||
}
|
||||
|
||||
int X64CodeCache::Initialize() {
|
||||
return 0;
|
||||
}
|
||||
int X64CodeCache::Initialize() { return 0; }
|
||||
|
||||
void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
|
||||
size_t stack_size) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
size_t alloc_size = code_size;
|
||||
|
||||
// Add unwind info into the allocation size. Keep things 16b aligned.
|
||||
code_size += XEROUNDUP(X64CodeChunk::UNWIND_INFO_SIZE, 16);
|
||||
alloc_size += poly::round_up(X64CodeChunk::UNWIND_INFO_SIZE, 16);
|
||||
|
||||
// Always move the code to land on 16b alignment. We do this by rounding up
|
||||
// to 16b so that all offsets are aligned.
|
||||
code_size = XEROUNDUP(code_size, 16);
|
||||
alloc_size = poly::round_up(alloc_size, 16);
|
||||
|
||||
LockMutex(lock_);
|
||||
lock_.lock();
|
||||
|
||||
if (active_chunk_) {
|
||||
if (active_chunk_->capacity - active_chunk_->offset < code_size) {
|
||||
if (active_chunk_->capacity - active_chunk_->offset < alloc_size) {
|
||||
auto next = active_chunk_->next;
|
||||
if (!next) {
|
||||
XEASSERT(code_size < chunk_size_); // need to support larger chunks
|
||||
assert_true(alloc_size < chunk_size_, "need to support larger chunks");
|
||||
next = new X64CodeChunk(chunk_size_);
|
||||
active_chunk_->next = next;
|
||||
}
|
||||
|
@ -101,40 +87,35 @@ void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
|
|||
}
|
||||
|
||||
uint8_t* final_address = active_chunk_->buffer + active_chunk_->offset;
|
||||
active_chunk_->offset += code_size;
|
||||
active_chunk_->offset += alloc_size;
|
||||
|
||||
// Add entry to fn table.
|
||||
active_chunk_->AddTableEntry(final_address, code_size, stack_size);
|
||||
active_chunk_->AddTableEntry(final_address, alloc_size, stack_size);
|
||||
|
||||
UnlockMutex(lock_);
|
||||
lock_.unlock();
|
||||
|
||||
// Copy code.
|
||||
xe_copy_struct(final_address, machine_code, code_size);
|
||||
memcpy(final_address, machine_code, code_size);
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), final_address, code_size);
|
||||
FlushInstructionCache(GetCurrentProcess(), final_address, alloc_size);
|
||||
return final_address;
|
||||
}
|
||||
|
||||
X64CodeChunk::X64CodeChunk(size_t chunk_size) :
|
||||
next(NULL),
|
||||
capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = (uint8_t*)VirtualAlloc(
|
||||
NULL, capacity,
|
||||
MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
X64CodeChunk::X64CodeChunk(size_t chunk_size)
|
||||
: next(NULL), capacity(chunk_size), buffer(0), offset(0) {
|
||||
buffer = (uint8_t*)VirtualAlloc(NULL, capacity, MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
|
||||
fn_table_capacity = (uint32_t)XEROUNDUP(capacity / ESTIMATED_FN_SIZE, 16);
|
||||
fn_table_capacity =
|
||||
static_cast<uint32_t>(poly::round_up(capacity / ESTIMATED_FN_SIZE, 16));
|
||||
size_t table_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
|
||||
fn_table = (RUNTIME_FUNCTION*)xe_malloc(table_size);
|
||||
fn_table = (RUNTIME_FUNCTION*)malloc(table_size);
|
||||
fn_table_count = 0;
|
||||
fn_table_handle = 0;
|
||||
RtlAddGrowableFunctionTable(
|
||||
&fn_table_handle,
|
||||
fn_table,
|
||||
fn_table_count,
|
||||
fn_table_capacity,
|
||||
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
|
||||
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
|
||||
fn_table_capacity, (ULONG_PTR)buffer,
|
||||
(ULONG_PTR)buffer + capacity);
|
||||
}
|
||||
|
||||
X64CodeChunk::~X64CodeChunk() {
|
||||
|
@ -160,7 +141,7 @@ typedef enum _UNWIND_OP_CODES {
|
|||
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
|
||||
} UNWIND_CODE_OPS;
|
||||
class UNWIND_REGISTER {
|
||||
public:
|
||||
public:
|
||||
enum _ {
|
||||
RAX = 0,
|
||||
RCX = 1,
|
||||
|
@ -185,25 +166,25 @@ typedef union _UNWIND_CODE {
|
|||
struct {
|
||||
uint8_t CodeOffset;
|
||||
uint8_t UnwindOp : 4;
|
||||
uint8_t OpInfo : 4;
|
||||
uint8_t OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE, *PUNWIND_CODE;
|
||||
|
||||
typedef struct _UNWIND_INFO {
|
||||
uint8_t Version : 3;
|
||||
uint8_t Flags : 5;
|
||||
uint8_t Version : 3;
|
||||
uint8_t Flags : 5;
|
||||
uint8_t SizeOfProlog;
|
||||
uint8_t CountOfCodes;
|
||||
uint8_t FrameRegister : 4;
|
||||
uint8_t FrameOffset : 4;
|
||||
uint8_t FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode[1];
|
||||
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
|
||||
* union {
|
||||
* OPTIONAL ULONG ExceptionHandler;
|
||||
* OPTIONAL ULONG FunctionEntry;
|
||||
* };
|
||||
* OPTIONAL ULONG ExceptionData[]; */
|
||||
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
|
||||
* union {
|
||||
* OPTIONAL ULONG ExceptionHandler;
|
||||
* OPTIONAL ULONG FunctionEntry;
|
||||
* };
|
||||
* OPTIONAL ULONG ExceptionData[]; */
|
||||
} UNWIND_INFO, *PUNWIND_INFO;
|
||||
} // namespace
|
||||
|
||||
|
@ -214,33 +195,27 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
|||
if (fn_table_count + 1 > fn_table_capacity) {
|
||||
// Table exhausted, need to realloc. If this happens a lot we should tune
|
||||
// the table size to prevent this.
|
||||
XELOGW("X64CodeCache growing FunctionTable - adjust ESTIMATED_FN_SIZE");
|
||||
PLOGW("X64CodeCache growing FunctionTable - adjust ESTIMATED_FN_SIZE");
|
||||
RtlDeleteGrowableFunctionTable(fn_table_handle);
|
||||
size_t old_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
|
||||
size_t new_size = old_size * 2;
|
||||
auto new_table = (RUNTIME_FUNCTION*)xe_realloc(fn_table, old_size, new_size);
|
||||
XEASSERTNOTNULL(new_table);
|
||||
auto new_table = (RUNTIME_FUNCTION*)realloc(fn_table, new_size);
|
||||
assert_not_null(new_table);
|
||||
if (!new_table) {
|
||||
return;
|
||||
}
|
||||
fn_table = new_table;
|
||||
fn_table_capacity *= 2;
|
||||
RtlAddGrowableFunctionTable(
|
||||
&fn_table_handle,
|
||||
fn_table,
|
||||
fn_table_count,
|
||||
fn_table_capacity,
|
||||
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
|
||||
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
|
||||
fn_table_capacity, (ULONG_PTR)buffer,
|
||||
(ULONG_PTR)buffer + capacity);
|
||||
}
|
||||
|
||||
// Allocate unwind data. We know we have space because we overallocated.
|
||||
// This should be the tailing 16b with 16b alignment.
|
||||
size_t unwind_info_offset = offset;
|
||||
offset += UNWIND_INFO_SIZE;
|
||||
size_t unwind_info_offset = offset - UNWIND_INFO_SIZE;
|
||||
|
||||
if (!stack_size) {
|
||||
uint8_t prolog_size = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
UNWIND_INFO* unwind_info = (UNWIND_INFO*)(buffer + unwind_info_offset);
|
||||
unwind_info->Version = 1;
|
||||
|
@ -264,7 +239,8 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
|||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset = 14; // end of instruction + 1 == offset of next instruction
|
||||
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;
|
||||
} else {
|
||||
|
@ -283,7 +259,8 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
|||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset = 7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.CodeOffset =
|
||||
7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code.OpInfo = 0;
|
||||
unwind_code = unwind_info->UnwindCode[co++];
|
||||
|
@ -299,3 +276,7 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
|||
// Notify the function table that it has new entries.
|
||||
RtlGrowFunctionTable(fn_table_handle, fn_table_count);
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
|
@ -7,76 +7,71 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_emitter.h>
|
||||
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include <alloy/backend/x64/x64_code_cache.h>
|
||||
#include <alloy/backend/x64/x64_function.h>
|
||||
#include <alloy/backend/x64/x64_sequences.h>
|
||||
#include <alloy/backend/x64/x64_thunk_emitter.h>
|
||||
#include <alloy/hir/hir_builder.h>
|
||||
#include <alloy/runtime/debug_info.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include <alloy/runtime/symbol_info.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
using namespace Xbyak;
|
||||
#include "alloy/backend/x64/x64_emitter.h"
|
||||
|
||||
#include "alloy/alloy-private.h"
|
||||
#include "alloy/backend/x64/x64_backend.h"
|
||||
#include "alloy/backend/x64/x64_code_cache.h"
|
||||
#include "alloy/backend/x64/x64_function.h"
|
||||
#include "alloy/backend/x64/x64_sequences.h"
|
||||
#include "alloy/backend/x64/x64_thunk_emitter.h"
|
||||
#include "alloy/hir/hir_builder.h"
|
||||
#include "alloy/runtime/debug_info.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "alloy/runtime/symbol_info.h"
|
||||
#include "alloy/runtime/thread_state.h"
|
||||
#include "xdb/protocol.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
using namespace Xbyak;
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::hir::Instr;
|
||||
using alloy::runtime::Function;
|
||||
using alloy::runtime::FunctionInfo;
|
||||
using alloy::runtime::SourceMapEntry;
|
||||
using alloy::runtime::ThreadState;
|
||||
|
||||
static const size_t MAX_CODE_SIZE = 1 * 1024 * 1024;
|
||||
|
||||
static const size_t STASH_OFFSET = 32;
|
||||
static const size_t STASH_OFFSET_HIGH = 32 + 32;
|
||||
|
||||
// If we are running with tracing on we have to store the EFLAGS in the stack,
|
||||
// otherwise our calls out to C to print will clear it before DID_CARRY/etc
|
||||
// can get the value.
|
||||
#define STORE_EFLAGS 1
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
|
||||
Operand::RBX,
|
||||
Operand::R12, Operand::R13, Operand::R14, Operand::R15,
|
||||
Operand::RBX, Operand::R12, Operand::R13, Operand::R14, Operand::R15,
|
||||
};
|
||||
|
||||
const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
|
||||
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
: CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator),
|
||||
runtime_(backend->runtime()),
|
||||
backend_(backend),
|
||||
code_cache_(backend->code_cache()),
|
||||
allocator_(allocator),
|
||||
current_instr_(0) {}
|
||||
|
||||
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) :
|
||||
runtime_(backend->runtime()),
|
||||
backend_(backend),
|
||||
code_cache_(backend->code_cache()),
|
||||
allocator_(allocator),
|
||||
current_instr_(0),
|
||||
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
|
||||
}
|
||||
X64Emitter::~X64Emitter() {}
|
||||
|
||||
X64Emitter::~X64Emitter() {
|
||||
}
|
||||
int X64Emitter::Initialize() { return 0; }
|
||||
|
||||
int X64Emitter::Initialize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X64Emitter::Emit(
|
||||
HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
void*& out_code_address, size_t& out_code_size) {
|
||||
int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
|
||||
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||
void*& out_code_address, size_t& out_code_size) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
// Reset.
|
||||
|
@ -84,6 +79,7 @@ int X64Emitter::Emit(
|
|||
source_map_count_ = 0;
|
||||
source_map_arena_.Reset();
|
||||
}
|
||||
trace_flags_ = trace_flags;
|
||||
|
||||
// Fill the generator with code.
|
||||
size_t stack_size = 0;
|
||||
|
@ -99,8 +95,7 @@ int X64Emitter::Emit(
|
|||
// Stash source map.
|
||||
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
|
||||
debug_info->InitializeSourceMap(
|
||||
source_map_count_,
|
||||
(SourceMapEntry*)source_map_arena_.CloneContents());
|
||||
source_map_count_, (SourceMapEntry*)source_map_arena_.CloneContents());
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -129,13 +124,13 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
auto slot = *it;
|
||||
size_t type_size = GetTypeSize(slot->type);
|
||||
// Align to natural size.
|
||||
stack_offset = XEALIGN(stack_offset, type_size);
|
||||
stack_offset = poly::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 = XEALIGN(stack_offset, 16);
|
||||
stack_offset = poly::align(stack_offset, static_cast<size_t>(16));
|
||||
|
||||
// Function prolog.
|
||||
// Must be 16b aligned.
|
||||
|
@ -150,7 +145,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
// Adding or changing anything here must be matched!
|
||||
const bool emit_prolog = true;
|
||||
const size_t stack_size = StackLayout::GUEST_STACK_SIZE + stack_offset;
|
||||
XEASSERT((stack_size + 8) % 16 == 0);
|
||||
assert_true((stack_size + 8) % 16 == 0);
|
||||
out_stack_size = stack_size;
|
||||
stack_size_ = stack_size;
|
||||
if (emit_prolog) {
|
||||
|
@ -158,7 +153,20 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx);
|
||||
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rdx);
|
||||
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
|
||||
mov(rdx, qword[rcx + 8]); // membase
|
||||
mov(rdx, qword[rcx + 8]); // membase
|
||||
}
|
||||
|
||||
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||
if (trace_base && trace_flags_ & TRACE_USER_CALLS) {
|
||||
mov(rax, trace_base);
|
||||
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::UserCallEvent)));
|
||||
lock();
|
||||
xadd(qword[rax], r8);
|
||||
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::USER_CALL) |
|
||||
(static_cast<uint64_t>(0) << 8) | (0ull << 32));
|
||||
mov(qword[r8], rax);
|
||||
EmitGetCurrentThreadId();
|
||||
mov(word[r8 + 2], ax);
|
||||
}
|
||||
|
||||
// Body.
|
||||
|
@ -175,10 +183,20 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
const Instr* instr = block->instr_head;
|
||||
while (instr) {
|
||||
const Instr* new_tail = instr;
|
||||
|
||||
// Special handling of TRACE_SOURCE.
|
||||
if (instr->opcode == &OPCODE_TRACE_SOURCE_info) {
|
||||
if (trace_flags_ & TRACE_SOURCE) {
|
||||
EmitTraceSource(instr);
|
||||
}
|
||||
instr = instr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!SelectSequence(*this, instr, &new_tail)) {
|
||||
// No sequence found!
|
||||
XEASSERTALWAYS();
|
||||
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
|
||||
assert_always();
|
||||
PLOGE("Unable to process HIR opcode %s", instr->opcode->name);
|
||||
break;
|
||||
}
|
||||
instr = new_tail;
|
||||
|
@ -189,6 +207,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
|
||||
// Function epilog.
|
||||
L("epilog");
|
||||
EmitTraceUserCallReturn();
|
||||
if (emit_prolog) {
|
||||
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
|
||||
add(rsp, (uint32_t)stack_size);
|
||||
|
@ -208,27 +227,166 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
|||
|
||||
void X64Emitter::MarkSourceOffset(const Instr* i) {
|
||||
auto entry = source_map_arena_.Alloc<SourceMapEntry>();
|
||||
entry->source_offset = i->src1.offset;
|
||||
entry->hir_offset = uint32_t(i->block->ordinal << 16) | i->ordinal;
|
||||
entry->code_offset = getSize();
|
||||
entry->source_offset = i->src1.offset;
|
||||
entry->hir_offset = uint32_t(i->block->ordinal << 16) | i->ordinal;
|
||||
entry->code_offset = getSize();
|
||||
source_map_count_++;
|
||||
}
|
||||
|
||||
void X64Emitter::EmitTraceSource(const Instr* instr) {
|
||||
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||
if (!trace_base || !(trace_flags_ & TRACE_SOURCE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(benvanik): make this a function call to some append fn.
|
||||
|
||||
uint8_t dest_reg_0 = instr->flags & 0xFF;
|
||||
uint8_t dest_reg_1 = instr->flags >> 8;
|
||||
|
||||
xdb::protocol::EventType event_type;
|
||||
size_t event_size = 0;
|
||||
if (dest_reg_0 == 100) {
|
||||
event_type = xdb::protocol::EventType::INSTR;
|
||||
event_size = sizeof(xdb::protocol::InstrEvent);
|
||||
} else if (dest_reg_1 == 100) {
|
||||
if (dest_reg_0 & (1 << 7)) {
|
||||
event_type = xdb::protocol::EventType::INSTR_R16;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR16);
|
||||
} else {
|
||||
event_type = xdb::protocol::EventType::INSTR_R8;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR8);
|
||||
}
|
||||
} else {
|
||||
if (dest_reg_0 & (1 << 7) && dest_reg_1 & (1 << 7)) {
|
||||
event_type = xdb::protocol::EventType::INSTR_R16_R16;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR16R16);
|
||||
} else if (dest_reg_0 & (1 << 7) && !(dest_reg_1 & (1 << 7))) {
|
||||
event_type = xdb::protocol::EventType::INSTR_R16_R8;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR16R8);
|
||||
} else if (!(dest_reg_0 & (1 << 7)) && dest_reg_1 & (1 << 7)) {
|
||||
event_type = xdb::protocol::EventType::INSTR_R8_R16;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR8R16);
|
||||
} else if (!(dest_reg_0 & (1 << 7)) && !(dest_reg_1 & (1 << 7))) {
|
||||
event_type = xdb::protocol::EventType::INSTR_R8_R8;
|
||||
event_size = sizeof(xdb::protocol::InstrEventR8R8);
|
||||
}
|
||||
}
|
||||
assert_not_zero(event_size);
|
||||
|
||||
mov(rax, trace_base);
|
||||
mov(r8d, static_cast<uint32_t>(event_size));
|
||||
lock();
|
||||
xadd(qword[rax], r8);
|
||||
// r8 is now the pointer where we can write our event.
|
||||
|
||||
// Write the header, which is the same for everything (pretty much).
|
||||
// Some event types ignore the dest reg, and that's fine.
|
||||
uint64_t qword_0 = static_cast<uint64_t>(event_type) |
|
||||
(static_cast<uint64_t>(dest_reg_0) << 8) |
|
||||
(instr->src1.offset << 32);
|
||||
mov(rax, qword_0);
|
||||
mov(qword[r8], rax);
|
||||
|
||||
// Write thread ID.
|
||||
EmitGetCurrentThreadId();
|
||||
mov(word[r8 + 2], ax);
|
||||
|
||||
switch (event_type) {
|
||||
default:
|
||||
case xdb::protocol::EventType::INSTR:
|
||||
break;
|
||||
case xdb::protocol::EventType::INSTR_R8:
|
||||
case xdb::protocol::EventType::INSTR_R16:
|
||||
if (dest_reg_0 & (1 << 7)) {
|
||||
EmitTraceSourceAppendValue(instr->src2.value, 8);
|
||||
} else {
|
||||
EmitTraceSourceAppendValue(instr->src2.value, 8);
|
||||
}
|
||||
break;
|
||||
case xdb::protocol::EventType::INSTR_R8_R8:
|
||||
case xdb::protocol::EventType::INSTR_R8_R16:
|
||||
case xdb::protocol::EventType::INSTR_R16_R8:
|
||||
case xdb::protocol::EventType::INSTR_R16_R16:
|
||||
mov(word[r8 + 8], dest_reg_0 | static_cast<uint16_t>(dest_reg_1 << 8));
|
||||
size_t offset = 8;
|
||||
if (dest_reg_0 & (1 << 7)) {
|
||||
EmitTraceSourceAppendValue(instr->src2.value, offset);
|
||||
offset += 16;
|
||||
} else {
|
||||
EmitTraceSourceAppendValue(instr->src2.value, offset);
|
||||
offset += 8;
|
||||
}
|
||||
if (dest_reg_1 & (1 << 7)) {
|
||||
EmitTraceSourceAppendValue(instr->src3.value, offset);
|
||||
offset += 16;
|
||||
} else {
|
||||
EmitTraceSourceAppendValue(instr->src3.value, offset);
|
||||
offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void X64Emitter::EmitTraceSourceAppendValue(const Value* value,
|
||||
size_t r8_offset) {
|
||||
//
|
||||
}
|
||||
|
||||
void X64Emitter::EmitGetCurrentThreadId() {
|
||||
// rcx must point to context. We could fetch from the stack if needed.
|
||||
mov(ax, word[rcx + runtime_->frontend()->context_info()->thread_id_offset()]);
|
||||
}
|
||||
|
||||
void X64Emitter::EmitTraceUserCallReturn() {
|
||||
auto trace_base = runtime_->memory()->trace_base();
|
||||
if (!trace_base || !(trace_flags_ & TRACE_USER_CALLS)) {
|
||||
return;
|
||||
}
|
||||
mov(rdx, rax);
|
||||
mov(rax, trace_base);
|
||||
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::UserCallReturnEvent)));
|
||||
lock();
|
||||
xadd(qword[rax], r8);
|
||||
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::USER_CALL_RETURN) |
|
||||
(static_cast<uint64_t>(0) << 8) | (0ull << 32));
|
||||
mov(qword[r8], rax);
|
||||
EmitGetCurrentThreadId();
|
||||
mov(word[r8 + 2], ax);
|
||||
mov(rax, rdx);
|
||||
ReloadEDX();
|
||||
}
|
||||
|
||||
void X64Emitter::DebugBreak() {
|
||||
// TODO(benvanik): notify debugger.
|
||||
db(0xCC);
|
||||
}
|
||||
|
||||
void X64Emitter::Trap() {
|
||||
// 0x0FE00014 is a 'debug print' where r3 = buffer r4 = length
|
||||
// TODO(benvanik): post software interrupt to debugger.
|
||||
db(0xCC);
|
||||
void X64Emitter::Trap(uint16_t trap_type) {
|
||||
switch (trap_type) {
|
||||
case 20:
|
||||
// 0x0FE00014 is a 'debug print' where r3 = buffer r4 = length
|
||||
// TODO(benvanik): debug print at runtime.
|
||||
break;
|
||||
case 0:
|
||||
case 22:
|
||||
// Always trap?
|
||||
// TODO(benvanik): post software interrupt to debugger.
|
||||
if (FLAGS_break_on_debugbreak) {
|
||||
db(0xCC);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PLOGW("Unknown trap type %d", trap_type);
|
||||
db(0xCC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
||||
// TODO(benvanik): notify debugger.
|
||||
db(0xCC);
|
||||
XEASSERTALWAYS();
|
||||
assert_always();
|
||||
}
|
||||
|
||||
// Total size of ResolveFunctionSymbol call site in bytes.
|
||||
|
@ -236,17 +394,6 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
|||
const size_t TOTAL_RESOLVE_SIZE = 27;
|
||||
const size_t ASM_OFFSET = 2 + 2 + 8 + 2 + 8;
|
||||
|
||||
// Length Assembly Byte Sequence
|
||||
// =================================================================================
|
||||
// 2 bytes 66 NOP 66 90H
|
||||
// 3 bytes NOP DWORD ptr [EAX] 0F 1F 00H
|
||||
// 4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
|
||||
// 5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
|
||||
// 6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
|
||||
// 7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
|
||||
// 8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
|
||||
// 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H
|
||||
|
||||
uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
|
@ -255,14 +402,19 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
|||
// Resolve function. This will demand compile as required.
|
||||
Function* fn = NULL;
|
||||
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
|
||||
XEASSERTNOTNULL(fn);
|
||||
assert_not_null(fn);
|
||||
auto x64_fn = static_cast<X64Function*>(fn);
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||
|
||||
// Overwrite the call site.
|
||||
// The return address points to ReloadRCX work after the call.
|
||||
// Overwrite the call site.
|
||||
// The return address points to ReloadRCX work after the call.
|
||||
#if XE_LIKE_WIN32
|
||||
uint64_t return_address = reinterpret_cast<uint64_t>(_ReturnAddress());
|
||||
#pragma pack(push, 1)
|
||||
#else
|
||||
uint64_t return_address =
|
||||
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
||||
#endif // XE_LIKE_WIN32
|
||||
#pragma pack(push, 1)
|
||||
struct Asm {
|
||||
uint16_t mov_rax;
|
||||
uint64_t rax_constant;
|
||||
|
@ -271,7 +423,8 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
|||
uint16_t call_rax;
|
||||
uint8_t mov_rcx[5];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
#pragma pack(pop)
|
||||
static_assert_size(Asm, TOTAL_RESOLVE_SIZE);
|
||||
Asm* code = reinterpret_cast<Asm*>(return_address - ASM_OFFSET);
|
||||
code->rax_constant = addr;
|
||||
code->call_rax = 0x9066;
|
||||
|
@ -280,10 +433,10 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
|||
return addr;
|
||||
}
|
||||
|
||||
void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info) {
|
||||
void X64Emitter::Call(const hir::Instr* instr,
|
||||
runtime::FunctionInfo* symbol_info) {
|
||||
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
|
||||
// Resolve address to the function to call and store in rax.
|
||||
// TODO(benvanik): caching/etc. For now this makes debugging easier.
|
||||
if (fn) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn->machine_code()));
|
||||
} else {
|
||||
|
@ -297,12 +450,15 @@ void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_inf
|
|||
// 5b
|
||||
ReloadECX();
|
||||
size_t total_size = getSize() - start;
|
||||
XEASSERT(total_size == TOTAL_RESOLVE_SIZE);
|
||||
assert_true(total_size == TOTAL_RESOLVE_SIZE);
|
||||
// EDX overwritten, don't bother reloading.
|
||||
}
|
||||
|
||||
// Actually jump/call to rax.
|
||||
if (instr->flags & CALL_TAIL) {
|
||||
// Since we skip the prolog we need to mark the return here.
|
||||
EmitTraceUserCallReturn();
|
||||
|
||||
// Pass the callers return address over.
|
||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
||||
|
||||
|
@ -315,18 +471,66 @@ void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_inf
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: slot count limited by short jump size.
|
||||
const int kICSlotCount = 4;
|
||||
const int kICSlotSize = 23;
|
||||
const uint64_t kICSlotInvalidTargetAddress = 0x0F0F0F0F0F0F0F0F;
|
||||
|
||||
uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
|
||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
|
||||
// TODO(benvanik): required?
|
||||
target_address &= 0xFFFFFFFF;
|
||||
assert_not_zero(target_address);
|
||||
|
||||
Function* fn = NULL;
|
||||
thread_state->runtime()->ResolveFunction(target_address, &fn);
|
||||
XEASSERTNOTNULL(fn);
|
||||
assert_not_null(fn);
|
||||
auto x64_fn = static_cast<X64Function*>(fn);
|
||||
return reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||
|
||||
// Add an IC slot, if there is room.
|
||||
#if XE_LIKE_WIN32
|
||||
uint64_t return_address = reinterpret_cast<uint64_t>(_ReturnAddress());
|
||||
#else
|
||||
uint64_t return_address =
|
||||
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
||||
#endif // XE_LIKE_WIN32
|
||||
#pragma pack(push, 1)
|
||||
struct Asm {
|
||||
uint16_t cmp_rdx;
|
||||
uint32_t address_constant;
|
||||
uint16_t jmp_next_slot;
|
||||
uint16_t mov_rax;
|
||||
uint64_t target_constant;
|
||||
uint8_t jmp_skip_resolve[5];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert_size(Asm, kICSlotSize);
|
||||
// The return address points to ReloadRCX work after the call.
|
||||
// To get the top of the table, look back a ways.
|
||||
uint64_t table_start = return_address - 12 - kICSlotSize * kICSlotCount;
|
||||
// NOTE: order matters here - we update the address BEFORE we switch the code
|
||||
// over to passing the compare.
|
||||
Asm* table_slot = reinterpret_cast<Asm*>(table_start);
|
||||
bool wrote_ic = false;
|
||||
for (int i = 0; i < kICSlotCount; ++i) {
|
||||
if (poly::atomic_cas(kICSlotInvalidTargetAddress, addr,
|
||||
&table_slot->target_constant)) {
|
||||
// Got slot! Just write the compare and we're done.
|
||||
table_slot->address_constant = static_cast<uint32_t>(target_address);
|
||||
wrote_ic = true;
|
||||
break;
|
||||
}
|
||||
++table_slot;
|
||||
}
|
||||
if (!wrote_ic) {
|
||||
// TODO(benvanik): log that IC table is full.
|
||||
}
|
||||
|
||||
// We need to return the target in rax so that it gets called.
|
||||
return addr;
|
||||
}
|
||||
|
||||
void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
||||
|
@ -336,15 +540,53 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
|||
je("epilog", CodeGenerator::T_NEAR);
|
||||
}
|
||||
|
||||
// Resolve address to the function to call and store in rax.
|
||||
// TODO(benvanik): caching/etc. For now this makes debugging easier.
|
||||
if (reg.getIdx() != rdx.getIdx()) {
|
||||
mov(rdx, reg);
|
||||
}
|
||||
|
||||
inLocalLabel();
|
||||
Xbyak::Label skip_resolve;
|
||||
|
||||
// TODO(benvanik): make empty tables skippable (cmp, jump right to resolve).
|
||||
|
||||
// IC table, initially empty.
|
||||
// This will get filled in as functions are resolved.
|
||||
// Note that we only have a limited cache, and once it's full all calls
|
||||
// will fall through.
|
||||
// TODO(benvanik): check miss rate when full and add a 2nd-level table?
|
||||
// 0000000264BD4DC3 81 FA 0F0F0F0F cmp edx,0F0F0F0Fh
|
||||
// 0000000264BD4DC9 75 0C jne 0000000264BD4DD7
|
||||
// 0000000264BD4DCB 48 B8 0F0F0F0F0F0F0F0F mov rax,0F0F0F0F0F0F0F0Fh
|
||||
// 0000000264BD4DD5 EB XXXXXXXX jmp 0000000264BD4E00
|
||||
size_t table_start = getSize();
|
||||
for (int i = 0; i < kICSlotCount; ++i) {
|
||||
// Compare target address with constant, if matches jump there.
|
||||
// Otherwise, fall through.
|
||||
// 6b
|
||||
cmp(edx, 0x0F0F0F0F);
|
||||
Xbyak::Label next_slot;
|
||||
// 2b
|
||||
jne(next_slot, T_SHORT);
|
||||
// Match! Load up rax and skip down to the jmp code.
|
||||
// 10b
|
||||
mov(rax, kICSlotInvalidTargetAddress);
|
||||
// 5b
|
||||
jmp(skip_resolve, T_NEAR);
|
||||
L(next_slot);
|
||||
}
|
||||
size_t table_size = getSize() - table_start;
|
||||
assert_true(table_size == kICSlotSize * kICSlotCount);
|
||||
|
||||
// Resolve address to the function to call and store in rax.
|
||||
// We fall through to this when there are no hits in the IC table.
|
||||
CallNative(ResolveFunctionAddress);
|
||||
|
||||
// Actually jump/call to rax.
|
||||
L(skip_resolve);
|
||||
if (instr->flags & CALL_TAIL) {
|
||||
// Since we skip the prolog we need to mark the return here.
|
||||
EmitTraceUserCallReturn();
|
||||
|
||||
// Pass the callers return address over.
|
||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
||||
|
||||
|
@ -355,17 +597,37 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
|||
mov(rdx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
|
||||
call(rax);
|
||||
}
|
||||
|
||||
outLocalLabel();
|
||||
}
|
||||
|
||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
||||
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
|
||||
XELOGW("undefined extern call to %.8X %s",
|
||||
symbol_info->address(),
|
||||
symbol_info->name());
|
||||
PLOGW("undefined extern call to %.8llX %s", symbol_info->address(),
|
||||
symbol_info->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info) {
|
||||
XEASSERT(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
|
||||
void X64Emitter::CallExtern(const hir::Instr* instr,
|
||||
const FunctionInfo* symbol_info) {
|
||||
assert_true(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
|
||||
|
||||
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||
if (trace_base & trace_flags_ & TRACE_EXTERN_CALLS) {
|
||||
mov(rax, trace_base);
|
||||
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallEvent)));
|
||||
lock();
|
||||
xadd(qword[rax], r8);
|
||||
// TODO(benvanik): get module/ordinal.
|
||||
uint32_t module_id = 0;
|
||||
uint32_t ordinal = 0;
|
||||
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL) |
|
||||
(static_cast<uint64_t>(0) << 8) | (module_id << 16) |
|
||||
(ordinal));
|
||||
mov(qword[r8], rax);
|
||||
EmitGetCurrentThreadId();
|
||||
mov(word[r8 + 2], ax);
|
||||
}
|
||||
|
||||
if (!symbol_info->extern_handler()) {
|
||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
||||
} else {
|
||||
|
@ -383,6 +645,19 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_
|
|||
ReloadEDX();
|
||||
// rax = host return
|
||||
}
|
||||
if (trace_base && trace_flags_ & TRACE_EXTERN_CALLS) {
|
||||
mov(rax, trace_base);
|
||||
mov(r8d,
|
||||
static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallReturnEvent)));
|
||||
lock();
|
||||
xadd(qword[rax], r8);
|
||||
mov(rax,
|
||||
static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL_RETURN) |
|
||||
(static_cast<uint64_t>(0) << 8) | (0));
|
||||
mov(qword[r8], rax);
|
||||
EmitGetCurrentThreadId();
|
||||
mov(word[r8 + 2], ax);
|
||||
}
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(void* fn) {
|
||||
|
@ -392,21 +667,22 @@ void X64Emitter::CallNative(void* fn) {
|
|||
ReloadEDX();
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context)) {
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context)) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0)) {
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0) {
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
|
||||
uint64_t arg0) {
|
||||
mov(rdx, arg0);
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
call(rax);
|
||||
|
@ -415,17 +691,17 @@ void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uin
|
|||
}
|
||||
|
||||
void X64Emitter::CallNativeSafe(void* fn) {
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
mov(rdx, reinterpret_cast<uint64_t>(fn));
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
mov(rdx, reinterpret_cast<uint64_t>(fn));
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
call(rax);
|
||||
ReloadECX();
|
||||
ReloadEDX();
|
||||
// rax = host return
|
||||
}
|
||||
|
||||
void X64Emitter::SetReturnAddress(uint64_t value) {
|
||||
|
@ -437,34 +713,45 @@ void X64Emitter::ReloadECX() {
|
|||
}
|
||||
|
||||
void X64Emitter::ReloadEDX() {
|
||||
mov(rdx, qword[rcx + 8]); // membase
|
||||
mov(rdx, qword[rcx + 8]); // membase
|
||||
}
|
||||
|
||||
// Len Assembly Byte Sequence
|
||||
// ============================================================================
|
||||
// 2b 66 NOP 66 90H
|
||||
// 3b NOP DWORD ptr [EAX] 0F 1F 00H
|
||||
// 4b NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
|
||||
// 5b NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
|
||||
// 6b 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
|
||||
// 7b NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
|
||||
// 8b NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
|
||||
// 9b 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H
|
||||
void X64Emitter::nop(size_t length) {
|
||||
// TODO(benvanik): fat nop
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
db(0x90);
|
||||
}
|
||||
}
|
||||
|
||||
void X64Emitter::LoadEflags() {
|
||||
#if STORE_EFLAGS
|
||||
mov(eax, dword[rsp + STASH_OFFSET]);
|
||||
push(rax);
|
||||
popf();
|
||||
btr(eax, 0);
|
||||
#else
|
||||
// EFLAGS already present.
|
||||
// EFLAGS already present.
|
||||
#endif // STORE_EFLAGS
|
||||
}
|
||||
|
||||
void X64Emitter::StoreEflags() {
|
||||
#if STORE_EFLAGS
|
||||
pushf();
|
||||
pop(qword[rsp + STASH_OFFSET]);
|
||||
pop(dword[rsp + STASH_OFFSET]);
|
||||
#else
|
||||
// EFLAGS should have CA set?
|
||||
// (so long as we don't fuck with it)
|
||||
// EFLAGS should have CA set?
|
||||
// (so long as we don't fuck with it)
|
||||
#endif // STORE_EFLAGS
|
||||
}
|
||||
|
||||
uint32_t X64Emitter::page_table_address() const {
|
||||
uint64_t addr = runtime_->memory()->page_table();
|
||||
return static_cast<uint32_t>(addr);
|
||||
}
|
||||
|
||||
bool X64Emitter::ConstantFitsIn32Reg(uint64_t v) {
|
||||
if ((v & ~0x7FFFFFFF) == 0) {
|
||||
// Fits under 31 bits, so just load using normal mov.
|
||||
|
@ -498,32 +785,79 @@ void X64Emitter::MovMem64(const RegExp& addr, uint64_t v) {
|
|||
|
||||
Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
||||
static const vec128_t xmm_consts[] = {
|
||||
/* XMMZero */ vec128f(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
/* XMMOne */ vec128f(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
|
||||
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u, 0x00000000u, 0x00000000u),
|
||||
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u, 0x00000000u, 0x00000000u),
|
||||
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
|
||||
/* XMMNormalizeX16Y16 */ vec128f(1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
|
||||
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
||||
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u),
|
||||
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu),
|
||||
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu, 0xFFFFFFFFu, 0x7FFFFFFFu),
|
||||
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu),
|
||||
/* XMMPermuteControl15 */ vec128b(15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15),
|
||||
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x0C000408u),
|
||||
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du, 0xFFFFFF0Cu, 0xFFFFFF0Fu),
|
||||
/* XMMOneOver255 */ vec128f(1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f),
|
||||
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu, 0x0000001Fu),
|
||||
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu, 0x000000FFu, 0x000000FFu),
|
||||
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0x00000000u),
|
||||
/* XMM255 */ vec128f(255.0f, 255.0f, 255.0f, 255.0f),
|
||||
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u, 0x80808080u, 0x80808080u),
|
||||
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u, 0x80008000u, 0x80008000u),
|
||||
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||
/* XMMZero */ vec128f(0.0f),
|
||||
/* XMMOne */ vec128f(1.0f),
|
||||
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
|
||||
/* XMMFFFF */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||
0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u,
|
||||
0x00000000u, 0x00000000u),
|
||||
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u,
|
||||
0x00000000u, 0x00000000u),
|
||||
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
|
||||
/* XMMNormalizeX16Y16 */ vec128f(
|
||||
1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
|
||||
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
||||
/* XMM3333 */ vec128f(3.0f, 3.0f, 3.0f, 3.0f),
|
||||
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u,
|
||||
0x80000000u, 0x80000000u),
|
||||
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u,
|
||||
0x00000000u, 0x80000000u),
|
||||
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu,
|
||||
0x7FFFFFFFu, 0x7FFFFFFFu),
|
||||
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu,
|
||||
0xFFFFFFFFu, 0x7FFFFFFFu),
|
||||
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u,
|
||||
0x08090A0Bu, 0x0C0D0E0Fu),
|
||||
/* XMMByteOrderMask */ vec128i(0x01000302u, 0x05040706u,
|
||||
0x09080B0Au, 0x0D0C0F0Eu),
|
||||
/* XMMPermuteControl15 */ vec128b(15),
|
||||
/* XMMPermuteByteMask */ vec128b(0x1F),
|
||||
/* XMMPackD3DCOLORSat */ vec128i(0x404000FFu),
|
||||
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||
0xFFFFFFFFu, 0x0C000408u),
|
||||
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du,
|
||||
0xFFFFFF0Cu, 0xFFFFFF0Fu),
|
||||
/* XMMPackFLOAT16_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||
0xFFFFFFFFu, 0x01000302u),
|
||||
/* XMMUnpackFLOAT16_2 */ vec128i(0x0D0C0F0Eu, 0xFFFFFFFFu,
|
||||
0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMPackFLOAT16_4 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||
0x05040706u, 0x01000302u),
|
||||
/* XMMUnpackFLOAT16_4 */ vec128i(0x09080B0Au, 0x0D0C0F0Eu,
|
||||
0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMPackSHORT_2Min */ vec128i(0x403F8001u),
|
||||
/* XMMPackSHORT_2Max */ vec128i(0x40407FFFu),
|
||||
/* XMMPackSHORT_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||
0xFFFFFFFFu, 0x01000504u),
|
||||
/* XMMUnpackSHORT_2 */ vec128i(0xFFFF0F0Eu, 0xFFFF0D0Cu,
|
||||
0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMOneOver255 */ vec128f(1.0f / 255.0f),
|
||||
/* XMMMaskEvenPI16 */ vec128i(0x0000FFFFu, 0x0000FFFFu,
|
||||
0x0000FFFFu, 0x0000FFFFu),
|
||||
/* XMMShiftMaskEvenPI16 */ vec128i(0x0000000Fu, 0x0000000Fu,
|
||||
0x0000000Fu, 0x0000000Fu),
|
||||
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu,
|
||||
0x0000001Fu, 0x0000001Fu),
|
||||
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu,
|
||||
0x000000FFu, 0x000000FFu),
|
||||
/* XMMSwapWordMask */ vec128i(0x03030303u, 0x03030303u,
|
||||
0x03030303u, 0x03030303u),
|
||||
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u,
|
||||
0xFFFFFFFFu, 0x00000000u),
|
||||
/* XMM255 */ vec128f(255.0f),
|
||||
/* XMMPI32 */ vec128i(32),
|
||||
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u,
|
||||
0x80808080u, 0x80808080u),
|
||||
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u,
|
||||
0x80008000u, 0x80008000u),
|
||||
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u,
|
||||
0x80000000u, 0x80000000u),
|
||||
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u,
|
||||
0x80000000u, 0x80000000u),
|
||||
/* XMMShortMinPS */ vec128f(SHRT_MIN),
|
||||
/* XMMShortMaxPS */ vec128f(SHRT_MAX),
|
||||
};
|
||||
// TODO(benvanik): cache base pointer somewhere? stack? It'd be nice to
|
||||
// prevent this move.
|
||||
|
@ -555,11 +889,11 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, float v) {
|
|||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} x = { v };
|
||||
} x = {v};
|
||||
if (!v) {
|
||||
// 0
|
||||
vpxor(dest, dest);
|
||||
} else if (x.i == ~0UL) {
|
||||
} else if (x.i == ~0U) {
|
||||
// 1111...
|
||||
vpcmpeqb(dest, dest);
|
||||
} else {
|
||||
|
@ -574,7 +908,7 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, double v) {
|
|||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} x = { v };
|
||||
} x = {v};
|
||||
if (!v) {
|
||||
// 0
|
||||
vpxor(dest, dest);
|
||||
|
@ -589,15 +923,12 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, double v) {
|
|||
}
|
||||
}
|
||||
|
||||
Address X64Emitter::StashXmm(const Xmm& r) {
|
||||
auto addr = ptr[rsp + STASH_OFFSET];
|
||||
Address X64Emitter::StashXmm(int index, const Xmm& r) {
|
||||
auto addr = ptr[rsp + STASH_OFFSET + (index * 16)];
|
||||
vmovups(addr, r);
|
||||
return addr;
|
||||
}
|
||||
|
||||
Address X64Emitter::StashXmm(const vec128_t& v) {
|
||||
auto addr = ptr[rsp + STASH_OFFSET];
|
||||
LoadConstantXmm(xmm0, v);
|
||||
vmovups(addr, xmm0);
|
||||
return addr;
|
||||
}
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,18 +10,21 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_EMITTER_H_
|
||||
#define ALLOY_BACKEND_X64_X64_EMITTER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include "alloy/hir/value.h"
|
||||
#include "third_party/xbyak/xbyak/xbyak.h"
|
||||
|
||||
#include <alloy/hir/value.h>
|
||||
|
||||
#include <third_party/xbyak/xbyak/xbyak.h>
|
||||
|
||||
XEDECLARECLASS2(alloy, hir, HIRBuilder);
|
||||
XEDECLARECLASS2(alloy, hir, Instr);
|
||||
XEDECLARECLASS2(alloy, runtime, DebugInfo);
|
||||
XEDECLARECLASS2(alloy, runtime, FunctionInfo);
|
||||
XEDECLARECLASS2(alloy, runtime, Runtime);
|
||||
XEDECLARECLASS2(alloy, runtime, SymbolInfo);
|
||||
namespace alloy {
|
||||
namespace hir {
|
||||
class HIRBuilder;
|
||||
class Instr;
|
||||
} // namespace hir
|
||||
namespace runtime {
|
||||
class DebugInfo;
|
||||
class FunctionInfo;
|
||||
class Runtime;
|
||||
class SymbolInfo;
|
||||
} // namespace runtime
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -31,47 +34,66 @@ class X64Backend;
|
|||
class X64CodeCache;
|
||||
|
||||
enum RegisterFlags {
|
||||
REG_DEST = (1 << 0),
|
||||
REG_ABCD = (1 << 1),
|
||||
REG_DEST = (1 << 0),
|
||||
REG_ABCD = (1 << 1),
|
||||
};
|
||||
|
||||
enum XmmConst {
|
||||
XMMZero = 0,
|
||||
XMMZero = 0,
|
||||
XMMOne,
|
||||
XMMNegativeOne,
|
||||
XMMFFFF,
|
||||
XMMMaskX16Y16,
|
||||
XMMFlipX16Y16,
|
||||
XMMFixX16Y16,
|
||||
XMMNormalizeX16Y16,
|
||||
XMM0001,
|
||||
XMM3301,
|
||||
XMM3333,
|
||||
XMMSignMaskPS,
|
||||
XMMSignMaskPD,
|
||||
XMMAbsMaskPS,
|
||||
XMMAbsMaskPD,
|
||||
XMMByteSwapMask,
|
||||
XMMByteOrderMask,
|
||||
XMMPermuteControl15,
|
||||
XMMPermuteByteMask,
|
||||
XMMPackD3DCOLORSat,
|
||||
XMMPackD3DCOLOR,
|
||||
XMMUnpackD3DCOLOR,
|
||||
XMMPackFLOAT16_2,
|
||||
XMMUnpackFLOAT16_2,
|
||||
XMMPackFLOAT16_4,
|
||||
XMMUnpackFLOAT16_4,
|
||||
XMMPackSHORT_2Min,
|
||||
XMMPackSHORT_2Max,
|
||||
XMMPackSHORT_2,
|
||||
XMMUnpackSHORT_2,
|
||||
XMMOneOver255,
|
||||
XMMMaskEvenPI16,
|
||||
XMMShiftMaskEvenPI16,
|
||||
XMMShiftMaskPS,
|
||||
XMMShiftByteMask,
|
||||
XMMSwapWordMask,
|
||||
XMMUnsignedDwordMax,
|
||||
XMM255,
|
||||
XMMPI32,
|
||||
XMMSignMaskI8,
|
||||
XMMSignMaskI16,
|
||||
XMMSignMaskI32,
|
||||
XMMSignMaskF32,
|
||||
XMMShortMinPS,
|
||||
XMMShortMaxPS,
|
||||
};
|
||||
|
||||
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
||||
class XbyakAllocator : public Xbyak::Allocator {
|
||||
public:
|
||||
virtual bool useProtect() const { return false; }
|
||||
public:
|
||||
virtual bool useProtect() const { return false; }
|
||||
};
|
||||
|
||||
class X64Emitter : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
public:
|
||||
X64Emitter(X64Backend* backend, XbyakAllocator* allocator);
|
||||
virtual ~X64Emitter();
|
||||
|
||||
|
@ -80,11 +102,11 @@ public:
|
|||
|
||||
int Initialize();
|
||||
|
||||
int Emit(hir::HIRBuilder* builder,
|
||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
||||
int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||
void*& out_code_address, size_t& out_code_size);
|
||||
|
||||
public:
|
||||
public:
|
||||
// Reserved: rsp
|
||||
// Scratch: rax/rcx/rdx
|
||||
// xmm0-2 (could be only xmm0 with some trickery)
|
||||
|
@ -117,29 +139,31 @@ public:
|
|||
void MarkSourceOffset(const hir::Instr* i);
|
||||
|
||||
void DebugBreak();
|
||||
void Trap();
|
||||
void Trap(uint16_t trap_type = 0);
|
||||
void UnimplementedInstr(const hir::Instr* i);
|
||||
void UnimplementedExtern(const hir::Instr* i);
|
||||
|
||||
void Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info);
|
||||
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
|
||||
void CallExtern(const hir::Instr* instr, const runtime::FunctionInfo* symbol_info);
|
||||
void CallExtern(const hir::Instr* instr,
|
||||
const runtime::FunctionInfo* symbol_info);
|
||||
void CallNative(void* fn);
|
||||
void CallNative(uint64_t(*fn)(void* raw_context));
|
||||
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0));
|
||||
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0);
|
||||
void CallNative(uint64_t (*fn)(void* raw_context));
|
||||
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0));
|
||||
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
|
||||
uint64_t arg0);
|
||||
void CallNativeSafe(void* fn);
|
||||
void SetReturnAddress(uint64_t value);
|
||||
void ReloadECX();
|
||||
void ReloadEDX();
|
||||
|
||||
void nop(size_t length = 1);
|
||||
|
||||
// TODO(benvanik): Label for epilog (don't use strings).
|
||||
|
||||
void LoadEflags();
|
||||
void StoreEflags();
|
||||
|
||||
uint32_t page_table_address() const;
|
||||
|
||||
// Moves a 64bit immediate into memory.
|
||||
bool ConstantFitsIn32Reg(uint64_t v);
|
||||
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
|
||||
|
@ -148,36 +172,39 @@ public:
|
|||
void LoadConstantXmm(Xbyak::Xmm dest, float v);
|
||||
void LoadConstantXmm(Xbyak::Xmm dest, double v);
|
||||
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
|
||||
Xbyak::Address StashXmm(const Xbyak::Xmm& r);
|
||||
Xbyak::Address StashXmm(const vec128_t& v);
|
||||
Xbyak::Address StashXmm(int index, const Xbyak::Xmm& r);
|
||||
|
||||
size_t stack_size() const { return stack_size_; }
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void* Emplace(size_t stack_size);
|
||||
int Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
||||
void EmitTraceSource(const hir::Instr* instr);
|
||||
void EmitTraceSourceAppendValue(const hir::Value* value, size_t r8_offset);
|
||||
void EmitGetCurrentThreadId();
|
||||
void EmitTraceUserCallReturn();
|
||||
|
||||
protected:
|
||||
protected:
|
||||
runtime::Runtime* runtime_;
|
||||
X64Backend* backend_;
|
||||
X64CodeCache* code_cache_;
|
||||
XbyakAllocator* allocator_;
|
||||
X64Backend* backend_;
|
||||
X64CodeCache* code_cache_;
|
||||
XbyakAllocator* allocator_;
|
||||
|
||||
hir::Instr* current_instr_;
|
||||
|
||||
size_t source_map_count_;
|
||||
Arena source_map_arena_;
|
||||
size_t source_map_count_;
|
||||
Arena source_map_arena_;
|
||||
|
||||
size_t stack_size_;
|
||||
size_t stack_size_;
|
||||
|
||||
uint32_t trace_flags_;
|
||||
|
||||
static const uint32_t gpr_reg_map_[GPR_COUNT];
|
||||
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_EMITTER_H_
|
||||
|
|
|
@ -7,23 +7,23 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_function.h>
|
||||
#include "alloy/backend/x64/x64_function.h"
|
||||
|
||||
#include <alloy/backend/x64/tracing.h>
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
#include "alloy/backend/x64/x64_backend.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "alloy/runtime/thread_state.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
using namespace alloy::runtime;
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
using alloy::runtime::Breakpoint;
|
||||
using alloy::runtime::Function;
|
||||
using alloy::runtime::FunctionInfo;
|
||||
using alloy::runtime::ThreadState;
|
||||
|
||||
X64Function::X64Function(FunctionInfo* symbol_info) :
|
||||
machine_code_(NULL), code_size_(0),
|
||||
Function(symbol_info) {
|
||||
}
|
||||
X64Function::X64Function(FunctionInfo* symbol_info)
|
||||
: Function(symbol_info), machine_code_(nullptr), code_size_(0) {}
|
||||
|
||||
X64Function::~X64Function() {
|
||||
// machine_code_ is freed by code cache.
|
||||
|
@ -34,20 +34,17 @@ void X64Function::Setup(void* machine_code, size_t code_size) {
|
|||
code_size_ = code_size;
|
||||
}
|
||||
|
||||
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) {
|
||||
return 0;
|
||||
}
|
||||
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) { return 0; }
|
||||
|
||||
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) {
|
||||
return 0;
|
||||
}
|
||||
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) { return 0; }
|
||||
|
||||
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
||||
auto backend = (X64Backend*)thread_state->runtime()->backend();
|
||||
auto thunk = backend->host_to_guest_thunk();
|
||||
thunk(
|
||||
machine_code_,
|
||||
thread_state->raw_context(),
|
||||
(void*)return_address);
|
||||
thunk(machine_code_, thread_state->raw_context(), (void*)return_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,18 +10,15 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
||||
#define ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include <alloy/runtime/function.h>
|
||||
#include <alloy/runtime/symbol_info.h>
|
||||
|
||||
#include "alloy/runtime/function.h"
|
||||
#include "alloy/runtime/symbol_info.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
class X64Function : public runtime::Function {
|
||||
public:
|
||||
public:
|
||||
X64Function(runtime::FunctionInfo* symbol_info);
|
||||
virtual ~X64Function();
|
||||
|
||||
|
@ -30,21 +27,19 @@ public:
|
|||
|
||||
void Setup(void* machine_code, size_t code_size);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual int AddBreakpointImpl(runtime::Breakpoint* breakpoint);
|
||||
virtual int RemoveBreakpointImpl(runtime::Breakpoint* breakpoint);
|
||||
virtual int CallImpl(runtime::ThreadState* thread_state,
|
||||
uint64_t return_address);
|
||||
|
||||
private:
|
||||
void* machine_code_;
|
||||
size_t code_size_;
|
||||
private:
|
||||
void* machine_code_;
|
||||
size_t code_size_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
||||
|
|
|
@ -133,7 +133,7 @@ struct ValueOp : Op<ValueOp<T, KEY_TYPE, REG_TYPE, CONST_TYPE, TAG>, KEY_TYPE> {
|
|||
bool is_constant;
|
||||
virtual bool ConstantFitsIn32Reg() const { return true; }
|
||||
const REG_TYPE& reg() const {
|
||||
XEASSERT(!is_constant);
|
||||
assert_true(!is_constant);
|
||||
return reg_;
|
||||
}
|
||||
operator const REG_TYPE&() const {
|
||||
|
@ -183,33 +183,37 @@ protected:
|
|||
|
||||
template <int TAG = -1>
|
||||
struct I8 : ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> {
|
||||
typedef ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> BASE;
|
||||
const int8_t constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.i8;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i8;
|
||||
}
|
||||
};
|
||||
template <int TAG = -1>
|
||||
struct I16 : ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> {
|
||||
typedef ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> BASE;
|
||||
const int16_t constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.i16;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i16;
|
||||
}
|
||||
};
|
||||
template <int TAG = -1>
|
||||
struct I32 : ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> {
|
||||
typedef ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> BASE;
|
||||
const int32_t constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.i32;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i32;
|
||||
}
|
||||
};
|
||||
template <int TAG = -1>
|
||||
struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
|
||||
typedef ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> BASE;
|
||||
const int64_t constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.i64;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i64;
|
||||
}
|
||||
bool ConstantFitsIn32Reg() const override {
|
||||
int64_t v = value->constant.i64;
|
||||
int64_t v = BASE::value->constant.i64;
|
||||
if ((v & ~0x7FFFFFFF) == 0) {
|
||||
// Fits under 31 bits, so just load using normal mov.
|
||||
return true;
|
||||
|
@ -222,23 +226,26 @@ struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
|
|||
};
|
||||
template <int TAG = -1>
|
||||
struct F32 : ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> {
|
||||
typedef ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> BASE;
|
||||
const float constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.f32;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.f32;
|
||||
}
|
||||
};
|
||||
template <int TAG = -1>
|
||||
struct F64 : ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> {
|
||||
typedef ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> BASE;
|
||||
const double constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.f64;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.f64;
|
||||
}
|
||||
};
|
||||
template <int TAG = -1>
|
||||
struct V128 : ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> {
|
||||
typedef ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> BASE;
|
||||
const vec128_t& constant() const {
|
||||
XEASSERT(is_constant);
|
||||
return value->constant.v128;
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.v128;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -308,6 +315,7 @@ 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;
|
||||
|
@ -316,7 +324,7 @@ protected:
|
|||
template <typename... Ti> friend struct SequenceFields;
|
||||
bool Load(const Instr* i, TagTable& tag_table) {
|
||||
if (InstrKey(i).value == key &&
|
||||
LoadDest(i, tag_table)) {
|
||||
BASE::LoadDest(i, tag_table)) {
|
||||
instr = i;
|
||||
return true;
|
||||
}
|
||||
|
@ -325,6 +333,7 @@ protected:
|
|||
};
|
||||
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;
|
||||
|
@ -335,7 +344,7 @@ protected:
|
|||
template <typename... Ti> friend struct SequenceFields;
|
||||
bool Load(const Instr* i, TagTable& tag_table) {
|
||||
if (InstrKey(i).value == key &&
|
||||
LoadDest(i, tag_table) &&
|
||||
BASE::LoadDest(i, tag_table) &&
|
||||
tag_table.CheckTag<SRC1>(i->src1)) {
|
||||
instr = i;
|
||||
src1.Load(i->src1);
|
||||
|
@ -346,6 +355,7 @@ protected:
|
|||
};
|
||||
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;
|
||||
|
@ -358,7 +368,7 @@ protected:
|
|||
template <typename... Ti> friend struct SequenceFields;
|
||||
bool Load(const Instr* i, TagTable& tag_table) {
|
||||
if (InstrKey(i).value == key &&
|
||||
LoadDest(i, tag_table) &&
|
||||
BASE::LoadDest(i, tag_table) &&
|
||||
tag_table.CheckTag<SRC1>(i->src1) &&
|
||||
tag_table.CheckTag<SRC2>(i->src2)) {
|
||||
instr = i;
|
||||
|
@ -371,6 +381,7 @@ protected:
|
|||
};
|
||||
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;
|
||||
|
@ -385,7 +396,7 @@ protected:
|
|||
template <typename... Ti> friend struct SequenceFields;
|
||||
bool Load(const Instr* i, TagTable& tag_table) {
|
||||
if (InstrKey(i).value == key &&
|
||||
LoadDest(i, tag_table) &&
|
||||
BASE::LoadDest(i, tag_table) &&
|
||||
tag_table.CheckTag<SRC1>(i->src1) &&
|
||||
tag_table.CheckTag<SRC2>(i->src2) &&
|
||||
tag_table.CheckTag<SRC3>(i->src3)) {
|
||||
|
@ -404,7 +415,6 @@ struct SequenceFields;
|
|||
template <typename I1>
|
||||
struct SequenceFields<I1> {
|
||||
I1 i1;
|
||||
typedef typename I1 I1Type;
|
||||
protected:
|
||||
template <typename SEQ, typename... Ti> friend struct Sequence;
|
||||
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
|
||||
|
@ -516,9 +526,15 @@ const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
|
|||
|
||||
template <typename SEQ, typename T>
|
||||
struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||
typedef Sequence<SingleSequence<SEQ, T>, T> BASE;
|
||||
typedef T EmitArgType;
|
||||
static const uint32_t head_key = T::key;
|
||||
static void Emit(X64Emitter& e, const EmitArgs& _) {
|
||||
// TODO(benvanik): find a way to do this cross-compiler.
|
||||
#if XE_COMPILER_MSVC
|
||||
static uint32_t head_key() { return T::key; }
|
||||
#else
|
||||
static constexpr uint32_t head_key() { return T::key; }
|
||||
#endif // XE_COMPILER_MSVC
|
||||
static void Emit(X64Emitter& e, const typename BASE::EmitArgs& _) {
|
||||
SEQ::Emit(e, _.i1);
|
||||
}
|
||||
|
||||
|
@ -542,12 +558,12 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.is_constant);
|
||||
assert_true(!i.src2.is_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<decltype(i.src1)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
|
||||
e.mov(temp, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
|
@ -560,7 +576,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
|
@ -584,9 +600,9 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.is_constant);
|
||||
assert_true(!i.src2.is_constant);
|
||||
if (i.dest == i.src2) {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
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);
|
||||
|
@ -599,7 +615,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
|
@ -608,7 +624,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
|
@ -617,7 +633,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.dest == i.src1) {
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
} else if (i.dest == i.src2) {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
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);
|
||||
|
@ -632,7 +648,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
static void EmitCommutativeBinaryXmmOp(
|
||||
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.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) {
|
||||
|
@ -647,7 +663,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
static void EmitAssociativeBinaryXmmOp(
|
||||
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.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) {
|
||||
|
@ -663,11 +679,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.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<decltype(i.src1)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
|
||||
e.mov(temp, i.src1.constant());
|
||||
reg_reg_fn(e, i.src2, temp);
|
||||
}
|
||||
|
@ -675,7 +691,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.src1, temp);
|
||||
}
|
||||
|
@ -688,11 +704,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
XEASSERT(!i.src2.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<decltype(i.src1)::reg_type>(e);
|
||||
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);
|
||||
}
|
||||
|
@ -700,7 +716,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
|||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()), false);
|
||||
} else {
|
||||
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
|
||||
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);
|
||||
}
|
||||
|
@ -721,11 +737,9 @@ static const tag_t TAG5 = 5;
|
|||
static const tag_t TAG6 = 6;
|
||||
static const tag_t TAG7 = 7;
|
||||
|
||||
typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*, const Instr**);
|
||||
|
||||
template <typename T>
|
||||
void Register() {
|
||||
sequence_table.insert({ T::head_key, T::Select });
|
||||
sequence_table.insert({ T::head_key(), T::Select });
|
||||
}
|
||||
template <typename T, typename Tn, typename... Ts>
|
||||
void Register() {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,9 +10,11 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
||||
#define ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
XEDECLARECLASS2(alloy, hir, Instr);
|
||||
namespace alloy {
|
||||
namespace hir {
|
||||
class Instr;
|
||||
} // namespace hir
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -20,14 +22,12 @@ namespace x64 {
|
|||
|
||||
class X64Emitter;
|
||||
|
||||
|
||||
void RegisterSequences();
|
||||
bool SelectSequence(X64Emitter& e, const hir::Instr* i, const hir::Instr** new_tail);
|
||||
|
||||
bool SelectSequence(X64Emitter& e, const hir::Instr* i,
|
||||
const hir::Instr** new_tail);
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
||||
|
|
|
@ -7,25 +7,20 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_thunk_emitter.h>
|
||||
#include "alloy/backend/x64/x64_thunk_emitter.h"
|
||||
|
||||
#include <third_party/xbyak/xbyak/xbyak.h>
|
||||
#include "third_party/xbyak/xbyak/xbyak.h"
|
||||
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::backend::x64;
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
using namespace Xbyak;
|
||||
|
||||
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
: X64Emitter(backend, allocator) {}
|
||||
|
||||
X64ThunkEmitter::X64ThunkEmitter(
|
||||
X64Backend* backend, XbyakAllocator* allocator) :
|
||||
X64Emitter(backend, allocator) {
|
||||
}
|
||||
|
||||
X64ThunkEmitter::~X64ThunkEmitter() {
|
||||
}
|
||||
X64ThunkEmitter::~X64ThunkEmitter() {}
|
||||
|
||||
HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
||||
// rcx = target
|
||||
|
@ -101,7 +96,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
|||
// rdx = target function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
// rsp + 0 = return address
|
||||
mov(qword[rsp + 8 * 2], rdx);
|
||||
|
@ -123,6 +118,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
|||
mov(rax, rdx);
|
||||
mov(rdx, r8);
|
||||
mov(r8, r9);
|
||||
mov(r9, r10);
|
||||
call(rax);
|
||||
|
||||
mov(rbx, qword[rsp + 48]);
|
||||
|
@ -143,3 +139,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
|||
void* fn = Emplace(stack_size);
|
||||
return (HostToGuestThunk)fn;
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -7,19 +7,16 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_X64_X64_THUNK_EMITTER_H_
|
||||
#define XENIA_CPU_X64_X64_THUNK_EMITTER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include <alloy/backend/x64/x64_backend.h>
|
||||
#include <alloy/backend/x64/x64_emitter.h>
|
||||
#ifndef ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||
#define ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||
|
||||
#include "alloy/backend/x64/x64_backend.h"
|
||||
#include "alloy/backend/x64/x64_emitter.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
/**
|
||||
* Stack Layout
|
||||
* ----------------------------
|
||||
|
@ -99,35 +96,34 @@ namespace x64 {
|
|||
* | |
|
||||
* | |
|
||||
* +------------------+
|
||||
* | scratch, 32b | rsp + 32
|
||||
* | scratch, 48b | rsp + 32
|
||||
* | |
|
||||
* +------------------+
|
||||
* | rcx / context | rsp + 64
|
||||
* | rcx / context | rsp + 80
|
||||
* +------------------+
|
||||
* | guest ret addr | rsp + 72
|
||||
* | guest ret addr | rsp + 88
|
||||
* +------------------+
|
||||
* | call ret addr | rsp + 80
|
||||
* | call ret addr | rsp + 96
|
||||
* +------------------+
|
||||
* ... locals ...
|
||||
* +------------------+
|
||||
* | (return address) |
|
||||
* +------------------+
|
||||
* +------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
class StackLayout {
|
||||
public:
|
||||
public:
|
||||
const static size_t THUNK_STACK_SIZE = 120;
|
||||
|
||||
const static size_t GUEST_STACK_SIZE = 88;
|
||||
const static size_t GUEST_RCX_HOME = 64;
|
||||
const static size_t GUEST_RET_ADDR = 72;
|
||||
const static size_t GUEST_CALL_RET_ADDR = 80;
|
||||
const static size_t GUEST_STACK_SIZE = 104;
|
||||
const static size_t GUEST_RCX_HOME = 80;
|
||||
const static size_t GUEST_RET_ADDR = 88;
|
||||
const static size_t GUEST_CALL_RET_ADDR = 96;
|
||||
};
|
||||
|
||||
|
||||
class X64ThunkEmitter : public X64Emitter {
|
||||
public:
|
||||
public:
|
||||
X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator);
|
||||
virtual ~X64ThunkEmitter();
|
||||
|
||||
|
@ -138,10 +134,8 @@ public:
|
|||
GuestToHostThunk EmitGuestToHostThunk();
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // XENIA_CPU_X64_X64_THUNK_EMITTER_H_
|
||||
#endif // ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/backend/x64/x64_tracers.h>
|
||||
#include "alloy/backend/x64/x64_tracers.h"
|
||||
|
||||
#include <alloy/backend/x64/x64_emitter.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
#include "alloy/backend/x64/x64_emitter.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "alloy/runtime/thread_state.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend::x64;
|
||||
|
@ -27,9 +27,12 @@ namespace x64 {
|
|||
#define TARGET_THREAD 1
|
||||
|
||||
#define IFLUSH() fflush(stdout)
|
||||
#define IPRINT if (thread_state->thread_id() == TARGET_THREAD) printf
|
||||
#define IPRINT \
|
||||
if (thread_state->thread_id() == TARGET_THREAD) printf
|
||||
#define DFLUSH() fflush(stdout)
|
||||
#define DPRINT DFLUSH(); if (thread_state->thread_id() == TARGET_THREAD) printf
|
||||
#define DPRINT \
|
||||
DFLUSH(); \
|
||||
if (thread_state->thread_id() == TARGET_THREAD) printf
|
||||
|
||||
uint32_t GetTracingMode() {
|
||||
uint32_t mode = 0;
|
||||
|
@ -50,151 +53,148 @@ void TraceString(void* raw_context, const char* str) {
|
|||
|
||||
void TraceContextLoadI8(void* raw_context, uint64_t offset, uint8_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = ctx i8 +%d\n", (int8_t)value, value, offset);
|
||||
DPRINT("%d (%X) = ctx i8 +%llu\n", (int8_t)value, value, offset);
|
||||
}
|
||||
void TraceContextLoadI16(void* raw_context, uint64_t offset, uint16_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = ctx i16 +%d\n", (int16_t)value, value, offset);
|
||||
DPRINT("%d (%X) = ctx i16 +%llu\n", (int16_t)value, value, offset);
|
||||
}
|
||||
void TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = ctx i32 +%d\n", (int32_t)value, value, offset);
|
||||
DPRINT("%d (%X) = ctx i32 +%llu\n", (int32_t)value, value, offset);
|
||||
}
|
||||
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%lld (%llX) = ctx i64 +%d\n", (int64_t)value, value, offset);
|
||||
DPRINT("%lld (%llX) = ctx i64 +%llu\n", (int64_t)value, value, offset);
|
||||
}
|
||||
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%e (%X) = ctx f32 +%d\n", value.m128_f32[0], value.m128_i32[0], offset);
|
||||
DPRINT("%e (%X) = ctx f32 +%llu\n", poly::m128_f32<0>(value),
|
||||
poly::m128_i32<0>(value), offset);
|
||||
}
|
||||
void TraceContextLoadF64(void* raw_context, uint64_t offset, __m128 value) {
|
||||
void TraceContextLoadF64(void* raw_context, uint64_t offset,
|
||||
const double* value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
union {
|
||||
double d;
|
||||
uint64_t x;
|
||||
} f;
|
||||
f.x = value.m128_i64[0];
|
||||
DPRINT("%lle (%llX) = ctx f64 +%d\n", f.d, value.m128_i64[0], offset);
|
||||
auto v = _mm_loadu_pd(value);
|
||||
DPRINT("%le (%llX) = ctx f64 +%llu\n", poly::m128_f64<0>(v),
|
||||
poly::m128_i64<0>(v), offset);
|
||||
}
|
||||
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%d\n",
|
||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
|
||||
offset);
|
||||
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%llu\n",
|
||||
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
|
||||
poly::m128_i32<2>(value), poly::m128_i32<3>(value), offset);
|
||||
}
|
||||
|
||||
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx i8 +%d = %d (%X)\n", offset, (int8_t)value, value);
|
||||
DPRINT("ctx i8 +%llu = %d (%X)\n", offset, (int8_t)value, value);
|
||||
}
|
||||
void TraceContextStoreI16(void* raw_context, uint64_t offset, uint16_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx i16 +%d = %d (%X)\n", offset, (int16_t)value, value);
|
||||
DPRINT("ctx i16 +%llu = %d (%X)\n", offset, (int16_t)value, value);
|
||||
}
|
||||
void TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx i32 +%d = %d (%X)\n", offset, (int32_t)value, value);
|
||||
DPRINT("ctx i32 +%llu = %d (%X)\n", offset, (int32_t)value, value);
|
||||
}
|
||||
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx i64 +%d = %lld (%llX)\n", offset, (int64_t)value, value);
|
||||
DPRINT("ctx i64 +%llu = %lld (%llX)\n", offset, (int64_t)value, value);
|
||||
}
|
||||
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx f32 +%d = %e (%X)\n", offset, value.m128_i32[0], value.m128_f32[0]);
|
||||
DPRINT("ctx f32 +%llu = %e (%X)\n", offset, poly::m128_f32<0>(value),
|
||||
poly::m128_i32<0>(value));
|
||||
}
|
||||
void TraceContextStoreF64(void* raw_context, uint64_t offset, __m128 value) {
|
||||
void TraceContextStoreF64(void* raw_context, uint64_t offset,
|
||||
const double* value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
union {
|
||||
double d;
|
||||
uint64_t x;
|
||||
} f;
|
||||
f.x = value.m128_i64[0];
|
||||
DPRINT("ctx f64 +%d = %lle (%llX)\n", offset, value.m128_i64[0], f.d);
|
||||
auto v = _mm_loadu_pd(value);
|
||||
DPRINT("ctx f64 +%llu = %le (%llX)\n", offset, poly::m128_f64<0>(v),
|
||||
poly::m128_i64<0>(v));
|
||||
}
|
||||
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("ctx v128 +%d = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", offset,
|
||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
|
||||
DPRINT("ctx v128 +%llu = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", offset,
|
||||
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
|
||||
poly::m128_i32<2>(value), poly::m128_i32<3>(value));
|
||||
}
|
||||
|
||||
void TraceMemoryLoadI8(void* raw_context, uint64_t address, uint8_t value) {
|
||||
void TraceMemoryLoadI8(void* raw_context, uint32_t address, uint8_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = load.i8 %.8X\n", (int8_t)value, value, address);
|
||||
}
|
||||
void TraceMemoryLoadI16(void* raw_context, uint64_t address, uint16_t value) {
|
||||
void TraceMemoryLoadI16(void* raw_context, uint32_t address, uint16_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = load.i16 %.8X\n", (int16_t)value, value, address);
|
||||
}
|
||||
void TraceMemoryLoadI32(void* raw_context, uint64_t address, uint32_t value) {
|
||||
void TraceMemoryLoadI32(void* raw_context, uint32_t address, uint32_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%d (%X) = load.i32 %.8X\n", (int32_t)value, value, address);
|
||||
}
|
||||
void TraceMemoryLoadI64(void* raw_context, uint64_t address, uint64_t value) {
|
||||
void TraceMemoryLoadI64(void* raw_context, uint32_t address, uint64_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%lld (%llX) = load.i64 %.8X\n", (int64_t)value, value, address);
|
||||
}
|
||||
void TraceMemoryLoadF32(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryLoadF32(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("%e (%X) = load.f32 %.8X\n", value.m128_f32[0], value.m128_i32[0], address);
|
||||
DPRINT("%e (%X) = load.f32 %.8X\n", poly::m128_f32<0>(value),
|
||||
poly::m128_i32<0>(value), address);
|
||||
}
|
||||
void TraceMemoryLoadF64(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryLoadF64(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
union {
|
||||
double d;
|
||||
uint64_t x;
|
||||
} f;
|
||||
f.x = value.m128_i64[0];
|
||||
DPRINT("%lle (%llX) = load.f64 %.8X\n", f.d, value.m128_i64[0], address);
|
||||
DPRINT("%le (%llX) = load.f64 %.8X\n", poly::m128_f64<0>(value),
|
||||
poly::m128_i64<0>(value), address);
|
||||
}
|
||||
void TraceMemoryLoadV128(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryLoadV128(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = load.v128 %.8X\n",
|
||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
|
||||
address);
|
||||
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
|
||||
poly::m128_i32<2>(value), poly::m128_i32<3>(value), address);
|
||||
}
|
||||
|
||||
void TraceMemoryStoreI8(void* raw_context, uint64_t address, uint8_t value) {
|
||||
void TraceMemoryStoreI8(void* raw_context, uint32_t address, uint8_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.i8 %.8X = %d (%X)\n", address, (int8_t)value, value);
|
||||
}
|
||||
void TraceMemoryStoreI16(void* raw_context, uint64_t address, uint16_t value) {
|
||||
void TraceMemoryStoreI16(void* raw_context, uint32_t address, uint16_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.i16 %.8X = %d (%X)\n", address, (int16_t)value, value);
|
||||
}
|
||||
void TraceMemoryStoreI32(void* raw_context, uint64_t address, uint32_t value) {
|
||||
void TraceMemoryStoreI32(void* raw_context, uint32_t address, uint32_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.i32 %.8X = %d (%X)\n", address, (int32_t)value, value);
|
||||
}
|
||||
void TraceMemoryStoreI64(void* raw_context, uint64_t address, uint64_t value) {
|
||||
void TraceMemoryStoreI64(void* raw_context, uint32_t address, uint64_t value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.i64 %.8X = %lld (%llX)\n", address, (int64_t)value, value);
|
||||
}
|
||||
void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryStoreF32(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.f32 %.8X = %e (%X)\n", address, value.m128_f32[0], value.m128_i32[0]);
|
||||
DPRINT("store.f32 %.8X = %e (%X)\n", address, poly::m128_f32<0>(value),
|
||||
poly::m128_i32<0>(value));
|
||||
}
|
||||
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryStoreF64(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
union {
|
||||
double d;
|
||||
uint64_t x;
|
||||
} f;
|
||||
f.x = value.m128_i64[0];
|
||||
DPRINT("store.f64 %.8X = %lle (%llX)\n", address, f.d, value.m128_i64[0]);
|
||||
DPRINT("store.f64 %.8X = %le (%llX)\n", address, poly::m128_f64<0>(value),
|
||||
poly::m128_i64<0>(value));
|
||||
}
|
||||
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value) {
|
||||
void TraceMemoryStoreV128(void* raw_context, uint32_t address, __m128 value) {
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", address,
|
||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
|
||||
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n",
|
||||
address, poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
|
||||
poly::m128_i32<2>(value), poly::m128_i32<3>(value));
|
||||
}
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,24 +10,8 @@
|
|||
#ifndef ALLOY_BACKEND_X64_X64_TRACERS_H_
|
||||
#define ALLOY_BACKEND_X64_X64_TRACERS_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#if XE_LIKE_WIN32
|
||||
#include <xmmintrin.h>
|
||||
#else
|
||||
typedef union __declspec(align(16)) __m128 {
|
||||
float m128_f32[4];
|
||||
uint64_t m128_u64[2];
|
||||
int8_t m128_i8[16];
|
||||
int16_t m128_i16[8];
|
||||
int32_t m128_i32[4];
|
||||
int64_t m128_i64[2];
|
||||
uint8_t m128_u8[16];
|
||||
uint16_t m128_u16[8];
|
||||
uint32_t m128_u32[4];
|
||||
} __m128;
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace alloy {
|
||||
namespace backend {
|
||||
|
@ -50,7 +34,8 @@ void TraceContextLoadI16(void* raw_context, uint64_t offset, uint16_t value);
|
|||
void TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value);
|
||||
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value);
|
||||
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 value);
|
||||
void TraceContextLoadF64(void* raw_context, uint64_t offset, __m128 value);
|
||||
void TraceContextLoadF64(void* raw_context, uint64_t offset,
|
||||
const double* value);
|
||||
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value);
|
||||
|
||||
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t value);
|
||||
|
@ -58,28 +43,28 @@ void TraceContextStoreI16(void* raw_context, uint64_t offset, uint16_t value);
|
|||
void TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value);
|
||||
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value);
|
||||
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 value);
|
||||
void TraceContextStoreF64(void* raw_context, uint64_t offset, __m128 value);
|
||||
void TraceContextStoreF64(void* raw_context, uint64_t offset,
|
||||
const double* value);
|
||||
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value);
|
||||
|
||||
void TraceMemoryLoadI8(void* raw_context, uint64_t address, uint8_t value);
|
||||
void TraceMemoryLoadI16(void* raw_context, uint64_t address, uint16_t value);
|
||||
void TraceMemoryLoadI32(void* raw_context, uint64_t address, uint32_t value);
|
||||
void TraceMemoryLoadI64(void* raw_context, uint64_t address, uint64_t value);
|
||||
void TraceMemoryLoadF32(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryLoadF64(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryLoadV128(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryLoadI8(void* raw_context, uint32_t address, uint8_t value);
|
||||
void TraceMemoryLoadI16(void* raw_context, uint32_t address, uint16_t value);
|
||||
void TraceMemoryLoadI32(void* raw_context, uint32_t address, uint32_t value);
|
||||
void TraceMemoryLoadI64(void* raw_context, uint32_t address, uint64_t value);
|
||||
void TraceMemoryLoadF32(void* raw_context, uint32_t address, __m128 value);
|
||||
void TraceMemoryLoadF64(void* raw_context, uint32_t address, __m128 value);
|
||||
void TraceMemoryLoadV128(void* raw_context, uint32_t address, __m128 value);
|
||||
|
||||
void TraceMemoryStoreI8(void* raw_context, uint64_t address, uint8_t value);
|
||||
void TraceMemoryStoreI16(void* raw_context, uint64_t address, uint16_t value);
|
||||
void TraceMemoryStoreI32(void* raw_context, uint64_t address, uint32_t value);
|
||||
void TraceMemoryStoreI64(void* raw_context, uint64_t address, uint64_t value);
|
||||
void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value);
|
||||
void TraceMemoryStoreI8(void* raw_context, uint32_t address, uint8_t value);
|
||||
void TraceMemoryStoreI16(void* raw_context, uint32_t address, uint16_t value);
|
||||
void TraceMemoryStoreI32(void* raw_context, uint32_t address, uint32_t value);
|
||||
void TraceMemoryStoreI64(void* raw_context, uint32_t address, uint64_t value);
|
||||
void TraceMemoryStoreF32(void* raw_context, uint32_t address, __m128 value);
|
||||
void TraceMemoryStoreF64(void* raw_context, uint32_t address, __m128 value);
|
||||
void TraceMemoryStoreV128(void* raw_context, uint32_t address, __m128 value);
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_BACKEND_X64_X64_TRACERS_H_
|
||||
|
|
|
@ -7,55 +7,36 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include "alloy/compiler/compiler.h"
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
#include <alloy/compiler/tracing.h>
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::runtime::Runtime;
|
||||
|
||||
Compiler::Compiler(Runtime* runtime) :
|
||||
runtime_(runtime) {
|
||||
scratch_arena_ = new Arena();
|
||||
Compiler::Compiler(Runtime* runtime) : runtime_(runtime) {}
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::Init({
|
||||
}));
|
||||
}
|
||||
Compiler::~Compiler() { Reset(); }
|
||||
|
||||
Compiler::~Compiler() {
|
||||
Reset();
|
||||
|
||||
for (auto it = passes_.begin(); it != passes_.end(); ++it) {
|
||||
CompilerPass* pass = *it;
|
||||
delete pass;
|
||||
}
|
||||
|
||||
delete scratch_arena_;
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::Deinit({
|
||||
}));
|
||||
}
|
||||
|
||||
void Compiler::AddPass(CompilerPass* pass) {
|
||||
void Compiler::AddPass(std::unique_ptr<CompilerPass> pass) {
|
||||
pass->Initialize(this);
|
||||
passes_.push_back(pass);
|
||||
passes_.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
void Compiler::Reset() {
|
||||
}
|
||||
void Compiler::Reset() {}
|
||||
|
||||
int Compiler::Compile(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
// TODO(benvanik): sophisticated stuff. Run passes in parallel, run until they
|
||||
// stop changing things, etc.
|
||||
for (auto it = passes_.begin(); it != passes_.end(); ++it) {
|
||||
CompilerPass* pass = *it;
|
||||
scratch_arena_->Reset();
|
||||
for (size_t i = 0; i < passes_.size(); ++i) {
|
||||
auto& pass = passes_[i];
|
||||
scratch_arena_.Reset();
|
||||
if (pass->Run(builder)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -63,3 +44,6 @@ int Compiler::Compile(HIRBuilder* builder) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,43 +10,44 @@
|
|||
#ifndef ALLOY_COMPILER_COMPILER_H_
|
||||
#define ALLOY_COMPILER_COMPILER_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
#include <alloy/hir/hir_builder.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace alloy { namespace runtime { class Runtime; } }
|
||||
#include "alloy/hir/hir_builder.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace runtime {
|
||||
class Runtime;
|
||||
} // namespace runtime
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
|
||||
class CompilerPass;
|
||||
|
||||
|
||||
class Compiler {
|
||||
public:
|
||||
public:
|
||||
Compiler(runtime::Runtime* runtime);
|
||||
~Compiler();
|
||||
|
||||
runtime::Runtime* runtime() const { return runtime_; }
|
||||
Arena* scratch_arena() const { return scratch_arena_; }
|
||||
Arena* scratch_arena() { return &scratch_arena_; }
|
||||
|
||||
void AddPass(CompilerPass* pass);
|
||||
void AddPass(std::unique_ptr<CompilerPass> pass);
|
||||
|
||||
void Reset();
|
||||
|
||||
int Compile(hir::HIRBuilder* builder);
|
||||
|
||||
private:
|
||||
private:
|
||||
runtime::Runtime* runtime_;
|
||||
Arena* scratch_arena_;
|
||||
Arena scratch_arena_;
|
||||
|
||||
typedef std::vector<CompilerPass*> PassList;
|
||||
PassList passes_;
|
||||
std::vector<std::unique_ptr<CompilerPass>> passes_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_COMPILER_H_
|
||||
|
|
|
@ -7,20 +7,16 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include "alloy/compiler/compiler.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::compiler;
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
|
||||
CompilerPass::CompilerPass() : runtime_(0), compiler_(0) {}
|
||||
|
||||
CompilerPass::CompilerPass() :
|
||||
runtime_(0), compiler_(0) {
|
||||
}
|
||||
|
||||
CompilerPass::~CompilerPass() {
|
||||
}
|
||||
CompilerPass::~CompilerPass() = default;
|
||||
|
||||
int CompilerPass::Initialize(Compiler* compiler) {
|
||||
runtime_ = compiler->runtime();
|
||||
|
@ -31,3 +27,6 @@ int CompilerPass::Initialize(Compiler* compiler) {
|
|||
Arena* CompilerPass::scratch_arena() const {
|
||||
return compiler_->scratch_arena();
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,21 +10,21 @@
|
|||
#ifndef ALLOY_COMPILER_COMPILER_PASS_H_
|
||||
#define ALLOY_COMPILER_COMPILER_PASS_H_
|
||||
|
||||
#include <alloy/core.h>
|
||||
|
||||
#include <alloy/hir/hir_builder.h>
|
||||
|
||||
namespace alloy { namespace runtime { class Runtime; } }
|
||||
#include "alloy/hir/hir_builder.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace runtime {
|
||||
class Runtime;
|
||||
} // namespace runtime
|
||||
} // namespace alloy
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
|
||||
class Compiler;
|
||||
|
||||
|
||||
class CompilerPass {
|
||||
public:
|
||||
public:
|
||||
CompilerPass();
|
||||
virtual ~CompilerPass();
|
||||
|
||||
|
@ -32,17 +32,15 @@ public:
|
|||
|
||||
virtual int Run(hir::HIRBuilder* builder) = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
Arena* scratch_arena() const;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
runtime::Runtime* runtime_;
|
||||
Compiler* compiler_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_COMPILER_PASS_H_
|
||||
|
|
|
@ -10,17 +10,18 @@
|
|||
#ifndef ALLOY_COMPILER_COMPILER_PASSES_H_
|
||||
#define ALLOY_COMPILER_COMPILER_PASSES_H_
|
||||
|
||||
#include <alloy/compiler/passes/constant_propagation_pass.h>
|
||||
#include <alloy/compiler/passes/control_flow_analysis_pass.h>
|
||||
#include <alloy/compiler/passes/context_promotion_pass.h>
|
||||
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
|
||||
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
||||
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
||||
#include <alloy/compiler/passes/finalization_pass.h>
|
||||
#include <alloy/compiler/passes/register_allocation_pass.h>
|
||||
#include <alloy/compiler/passes/simplification_pass.h>
|
||||
#include <alloy/compiler/passes/validation_pass.h>
|
||||
#include <alloy/compiler/passes/value_reduction_pass.h>
|
||||
#include "alloy/compiler/passes/constant_propagation_pass.h"
|
||||
#include "alloy/compiler/passes/context_promotion_pass.h"
|
||||
#include "alloy/compiler/passes/control_flow_analysis_pass.h"
|
||||
#include "alloy/compiler/passes/control_flow_simplification_pass.h"
|
||||
#include "alloy/compiler/passes/data_flow_analysis_pass.h"
|
||||
#include "alloy/compiler/passes/dead_code_elimination_pass.h"
|
||||
//#include "alloy/compiler/passes/dead_store_elimination_pass.h"
|
||||
#include "alloy/compiler/passes/finalization_pass.h"
|
||||
#include "alloy/compiler/passes/register_allocation_pass.h"
|
||||
#include "alloy/compiler/passes/simplification_pass.h"
|
||||
#include "alloy/compiler/passes/validation_pass.h"
|
||||
#include "alloy/compiler/passes/value_reduction_pass.h"
|
||||
|
||||
// TODO:
|
||||
// - mark_use/mark_set
|
||||
|
@ -49,7 +50,6 @@
|
|||
// Block gets:
|
||||
// AddIncomingValue(Value* value, Block* src_block) ??
|
||||
|
||||
|
||||
// Potentially interesting passes:
|
||||
//
|
||||
// Run order:
|
||||
|
|
|
@ -7,23 +7,26 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/constant_propagation_pass.h>
|
||||
#include "alloy/compiler/passes/constant_propagation_pass.h"
|
||||
|
||||
#include <alloy/runtime/function.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include "alloy/runtime/function.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::hir::TypeName;
|
||||
using alloy::hir::Value;
|
||||
|
||||
ConstantPropagationPass::ConstantPropagationPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
ConstantPropagationPass::ConstantPropagationPass() : CompilerPass() {}
|
||||
|
||||
ConstantPropagationPass::~ConstantPropagationPass() {
|
||||
}
|
||||
ConstantPropagationPass::~ConstantPropagationPass() {}
|
||||
|
||||
int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
@ -55,379 +58,408 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
// v1 = 19
|
||||
// v2 = 0
|
||||
|
||||
Block* block = builder->first_block();
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
Instr* i = block->instr_head;
|
||||
auto i = block->instr_head;
|
||||
while (i) {
|
||||
Value* v = i->dest;
|
||||
auto v = i->dest;
|
||||
switch (i->opcode->num) {
|
||||
case OPCODE_DEBUG_BREAK_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
i->Replace(&OPCODE_DEBUG_BREAK_info, i->flags);
|
||||
} else {
|
||||
i->Remove();
|
||||
case OPCODE_DEBUG_BREAK_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
i->Replace(&OPCODE_DEBUG_BREAK_info, i->flags);
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case OPCODE_TRAP_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
i->Replace(&OPCODE_TRAP_info, i->flags);
|
||||
} else {
|
||||
i->Remove();
|
||||
case OPCODE_TRAP_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
i->Replace(&OPCODE_TRAP_info, i->flags);
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case OPCODE_CALL_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto symbol_info = i->src2.symbol_info;
|
||||
case OPCODE_CALL_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto symbol_info = i->src2.symbol_info;
|
||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||
i->src1.symbol_info = symbol_info;
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
runtime::FunctionInfo* symbol_info;
|
||||
if (runtime_->LookupFunctionInfo(
|
||||
(uint32_t)i->src1.value->constant.i32, &symbol_info)) {
|
||||
break;
|
||||
}
|
||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||
i->src1.symbol_info = symbol_info;
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto value = i->src2.value;
|
||||
i->Replace(&OPCODE_CALL_INDIRECT_info, i->flags);
|
||||
i->set_src1(value);
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_BRANCH_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto label = i->src2.label;
|
||||
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
||||
i->src1.label = label;
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_BRANCH_FALSE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantFalse()) {
|
||||
auto label = i->src2.label;
|
||||
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
||||
i->src1.label = label;
|
||||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_CAST:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->Cast(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
runtime::FunctionInfo* symbol_info;
|
||||
if (runtime_->LookupFunctionInfo(
|
||||
(uint32_t)i->src1.value->constant.i32, &symbol_info)) {
|
||||
break;
|
||||
}
|
||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||
i->src1.symbol_info = symbol_info;
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto value = i->src2.value;
|
||||
i->Replace(&OPCODE_CALL_INDIRECT_info, i->flags);
|
||||
i->set_src1(value);
|
||||
} else {
|
||||
break;
|
||||
case OPCODE_ZERO_EXTEND:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->ZeroExtend(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_BRANCH_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
auto label = i->src2.label;
|
||||
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
||||
i->src1.label = label;
|
||||
} else {
|
||||
break;
|
||||
case OPCODE_SIGN_EXTEND:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->SignExtend(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_BRANCH_FALSE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantFalse()) {
|
||||
auto label = i->src2.label;
|
||||
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
||||
i->src1.label = label;
|
||||
} else {
|
||||
break;
|
||||
case OPCODE_TRUNCATE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->Truncate(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case OPCODE_CAST:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->Cast(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_ZERO_EXTEND:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->ZeroExtend(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SIGN_EXTEND:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->SignExtend(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_TRUNCATE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
TypeName target_type = v->type;
|
||||
v->set_from(i->src1.value);
|
||||
v->Truncate(target_type);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_SELECT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
v->set_from(i->src2.value);
|
||||
} else {
|
||||
v->set_from(i->src3.value);
|
||||
case OPCODE_SELECT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
v->set_from(i->src2.value);
|
||||
} else {
|
||||
v->set_from(i->src3.value);
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_IS_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
v->set_constant((int8_t)1);
|
||||
} else {
|
||||
v->set_constant((int8_t)0);
|
||||
break;
|
||||
case OPCODE_IS_TRUE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
v->set_constant((int8_t)1);
|
||||
} else {
|
||||
v->set_constant((int8_t)0);
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_IS_FALSE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantFalse()) {
|
||||
v->set_constant((int8_t)1);
|
||||
} else {
|
||||
v->set_constant((int8_t)0);
|
||||
break;
|
||||
case OPCODE_IS_FALSE:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->IsConstantFalse()) {
|
||||
v->set_constant((int8_t)1);
|
||||
} else {
|
||||
v->set_constant((int8_t)0);
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// TODO(benvanik): compares
|
||||
case OPCODE_COMPARE_EQ:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantEQ(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_NE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantNE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SLT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSLT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SLE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSLE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSGT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSGE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantULT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantULE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantUGT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantUGE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_DID_CARRY:
|
||||
XEASSERT(!i->src1.value->IsConstant());
|
||||
break;
|
||||
case OPCODE_DID_OVERFLOW:
|
||||
XEASSERT(!i->src1.value->IsConstant());
|
||||
break;
|
||||
case OPCODE_DID_SATURATE:
|
||||
XEASSERT(!i->src1.value->IsConstant());
|
||||
break;
|
||||
|
||||
case OPCODE_ADD:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
bool did_carry = v->Add(i->src2.value);
|
||||
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||
i->Remove();
|
||||
|
||||
// If carry is set find the DID_CARRY and fix it.
|
||||
if (propagate_carry) {
|
||||
PropagateCarry(v, did_carry);
|
||||
// TODO(benvanik): compares
|
||||
case OPCODE_COMPARE_EQ:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantEQ(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): ADD_CARRY (w/ ARITHMETIC_SET_CARRY)
|
||||
case OPCODE_SUB:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
bool did_carry = v->Sub(i->src2.value);
|
||||
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||
i->Remove();
|
||||
|
||||
// If carry is set find the DID_CARRY and fix it.
|
||||
if (propagate_carry) {
|
||||
PropagateCarry(v, did_carry);
|
||||
break;
|
||||
case OPCODE_COMPARE_NE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantNE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_MUL:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Mul(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_DIV:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Div(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// case OPCODE_MUL_ADD:
|
||||
// case OPCODE_MUL_SUB
|
||||
case OPCODE_NEG:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Neg();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_ABS:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Abs();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SQRT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Sqrt();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_RSQRT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->RSqrt();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case OPCODE_COMPARE_SLT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSLT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SLE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSLE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSGT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantSGE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantULT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantULE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGT:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantUGT(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
bool value = i->src1.value->IsConstantUGE(i->src2.value);
|
||||
i->dest->set_constant(value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_AND:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->And(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_OR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Or(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_XOR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Xor(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_NOT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Not();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHL:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Shl(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): VECTOR_SHL
|
||||
case OPCODE_SHR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Shr(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHA:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Sha(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): ROTATE_LEFT
|
||||
case OPCODE_BYTE_SWAP:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->ByteSwap();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_CNTLZ:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_zero(v->type);
|
||||
v->CountLeadingZeros(i->src1.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): INSERT/EXTRACT
|
||||
// TODO(benvanik): SPLAT/PERMUTE/SWIZZLE
|
||||
case OPCODE_SPLAT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
// Quite a few of these, from building vec128s.
|
||||
}
|
||||
break;
|
||||
case OPCODE_DID_CARRY:
|
||||
assert_true(!i->src1.value->IsConstant());
|
||||
break;
|
||||
case OPCODE_DID_OVERFLOW:
|
||||
assert_true(!i->src1.value->IsConstant());
|
||||
break;
|
||||
case OPCODE_DID_SATURATE:
|
||||
assert_true(!i->src1.value->IsConstant());
|
||||
break;
|
||||
|
||||
case OPCODE_ADD:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
bool did_carry = v->Add(i->src2.value);
|
||||
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||
i->Remove();
|
||||
|
||||
// If carry is set find the DID_CARRY and fix it.
|
||||
if (propagate_carry) {
|
||||
PropagateCarry(v, did_carry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): ADD_CARRY (w/ ARITHMETIC_SET_CARRY)
|
||||
case OPCODE_ADD_CARRY:
|
||||
if (i->src1.value->IsConstantZero() && i->src2.value->IsConstantZero()) {
|
||||
Value* ca = i->src3.value;
|
||||
// If carry is set find the DID_CARRY and fix it.
|
||||
if (!!(i->flags & ARITHMETIC_SET_CARRY)) {
|
||||
auto next = i->dest->use_head;
|
||||
while (next) {
|
||||
auto use = next;
|
||||
next = use->next;
|
||||
if (use->instr->opcode == &OPCODE_DID_CARRY_info) {
|
||||
// Replace carry value.
|
||||
use->instr->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
use->instr->set_src1(ca);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i->dest->type == ca->type) {
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(ca);
|
||||
} else {
|
||||
i->Replace(&OPCODE_ZERO_EXTEND_info, 0);
|
||||
i->set_src1(ca);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_SUB:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
bool did_carry = v->Sub(i->src2.value);
|
||||
bool propagate_carry = !!(i->flags & ARITHMETIC_SET_CARRY);
|
||||
i->Remove();
|
||||
|
||||
// If carry is set find the DID_CARRY and fix it.
|
||||
if (propagate_carry) {
|
||||
PropagateCarry(v, did_carry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPCODE_MUL:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Mul(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_DIV:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Div(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// case OPCODE_MUL_ADD:
|
||||
// case OPCODE_MUL_SUB
|
||||
case OPCODE_NEG:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Neg();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_ABS:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Abs();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SQRT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Sqrt();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_RSQRT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->RSqrt();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_AND:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->And(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_OR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Or(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_XOR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Xor(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_NOT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Not();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHL:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Shl(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): VECTOR_SHL
|
||||
case OPCODE_SHR:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Shr(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHA:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->Sha(i->src2.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): ROTATE_LEFT
|
||||
case OPCODE_BYTE_SWAP:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
v->ByteSwap();
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
case OPCODE_CNTLZ:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
v->set_zero(v->type);
|
||||
v->CountLeadingZeros(i->src1.value);
|
||||
i->Remove();
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): INSERT/EXTRACT
|
||||
// TODO(benvanik): SPLAT/PERMUTE/SWIZZLE
|
||||
case OPCODE_SPLAT:
|
||||
if (i->src1.value->IsConstant()) {
|
||||
// Quite a few of these, from building vec128s.
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignored.
|
||||
break;
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
|
@ -438,7 +470,7 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
|
||||
void ConstantPropagationPass::PropagateCarry(Value* v, bool did_carry) {
|
||||
auto next = v->use_head;
|
||||
while (next) {
|
||||
auto use = next;
|
||||
|
@ -450,3 +482,7 @@ void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,29 +10,25 @@
|
|||
#ifndef ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class ConstantPropagationPass : public CompilerPass {
|
||||
public:
|
||||
public:
|
||||
ConstantPropagationPass();
|
||||
virtual ~ConstantPropagationPass();
|
||||
~ConstantPropagationPass() override;
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
private:
|
||||
void PropagateCarry(hir::Value* v, bool did_carry);
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||
|
|
|
@ -7,35 +7,33 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/context_promotion_pass.h>
|
||||
#include "alloy/compiler/passes/context_promotion_pass.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
using namespace alloy::frontend;
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
#include "alloy/compiler/compiler.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
DEFINE_bool(store_all_context_values, false,
|
||||
"Don't strip dead context stores to aid in debugging.");
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
ContextPromotionPass::ContextPromotionPass() :
|
||||
context_values_size_(0), context_values_(0),
|
||||
CompilerPass() {
|
||||
}
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
|
||||
ContextPromotionPass::~ContextPromotionPass() {
|
||||
if (context_values_) {
|
||||
xe_free(context_values_);
|
||||
}
|
||||
}
|
||||
using alloy::frontend::ContextInfo;
|
||||
using alloy::hir::Block;
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::hir::Instr;
|
||||
using alloy::hir::Value;
|
||||
|
||||
ContextPromotionPass::ContextPromotionPass() : CompilerPass() {}
|
||||
|
||||
ContextPromotionPass::~ContextPromotionPass() {}
|
||||
|
||||
int ContextPromotionPass::Initialize(Compiler* compiler) {
|
||||
if (CompilerPass::Initialize(compiler)) {
|
||||
|
@ -44,8 +42,8 @@ int ContextPromotionPass::Initialize(Compiler* compiler) {
|
|||
|
||||
// This is a terrible implementation.
|
||||
ContextInfo* context_info = runtime_->frontend()->context_info();
|
||||
context_values_size_ = context_info->size() * sizeof(Value*);
|
||||
context_values_ = (Value**)xe_calloc(context_values_size_);
|
||||
context_values_.resize(context_info->size());
|
||||
context_validity_.resize(static_cast<uint32_t>(context_info->size()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -70,7 +68,7 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
|
|||
|
||||
// Promote loads to values.
|
||||
// Process each block independently, for now.
|
||||
Block* block = builder->first_block();
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
PromoteBlock(block);
|
||||
block = block->next;
|
||||
|
@ -89,50 +87,55 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
|
|||
}
|
||||
|
||||
void ContextPromotionPass::PromoteBlock(Block* block) {
|
||||
// Clear the context values list.
|
||||
// TODO(benvanik): new data structure that isn't so stupid.
|
||||
// Bitvector of validity, perhaps?
|
||||
xe_zero_struct(context_values_, context_values_size_);
|
||||
auto& validity = context_validity_;
|
||||
validity.reset();
|
||||
|
||||
Instr* i = block->instr_head;
|
||||
while (i) {
|
||||
if (i->opcode == &OPCODE_LOAD_CONTEXT_info) {
|
||||
auto next = i->next;
|
||||
if (i->opcode->flags & OPCODE_FLAG_VOLATILE) {
|
||||
// Volatile instruction - requires all context values be flushed.
|
||||
validity.reset();
|
||||
} else if (i->opcode == &OPCODE_LOAD_CONTEXT_info) {
|
||||
size_t offset = i->src1.offset;
|
||||
Value* previous_value = context_values_[offset];
|
||||
if (previous_value) {
|
||||
if (validity.test(static_cast<uint32_t>(offset))) {
|
||||
// Legit previous value, reuse.
|
||||
Value* previous_value = context_values_[offset];
|
||||
i->opcode = &hir::OPCODE_ASSIGN_info;
|
||||
i->set_src1(previous_value);
|
||||
} else {
|
||||
// Store the loaded value into the table.
|
||||
context_values_[offset] = i->dest;
|
||||
validity.set(static_cast<uint32_t>(offset));
|
||||
}
|
||||
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
|
||||
size_t offset = i->src1.offset;
|
||||
Value* value = i->src2.value;
|
||||
// Store value into the table for later.
|
||||
context_values_[offset] = value;
|
||||
validity.set(static_cast<uint32_t>(offset));
|
||||
}
|
||||
|
||||
i = i->next;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
|
||||
// TODO(benvanik): use a bitvector.
|
||||
// To avoid clearing the structure, we use a token.
|
||||
Value* token = (Value*)block;
|
||||
auto& validity = context_validity_;
|
||||
validity.reset();
|
||||
|
||||
// Walk backwards and mark offsets that are written to.
|
||||
// If the offset was written to earlier, ignore the store.
|
||||
Instr* i = block->instr_tail;
|
||||
while (i) {
|
||||
Instr* prev = i->prev;
|
||||
if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
|
||||
if (i->opcode->flags & (OPCODE_FLAG_VOLATILE | OPCODE_FLAG_BRANCH)) {
|
||||
// Volatile instruction - requires all context values be flushed.
|
||||
validity.reset();
|
||||
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
|
||||
size_t offset = i->src1.offset;
|
||||
if (context_values_[offset] != token) {
|
||||
// Mark offset as written to.
|
||||
context_values_[offset] = token;
|
||||
if (!validity.test(static_cast<uint32_t>(offset))) {
|
||||
// Offset not yet written, mark and continue.
|
||||
validity.set(static_cast<uint32_t>(offset));
|
||||
} else {
|
||||
// Already written to. Remove this store.
|
||||
i->Remove();
|
||||
|
@ -141,3 +144,7 @@ void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
|
|||
i = prev;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,36 +10,43 @@
|
|||
#ifndef ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
#if XE_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4267)
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#include <cmath>
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#endif // XE_COMPILER_MSVC
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class ContextPromotionPass : public CompilerPass {
|
||||
public:
|
||||
public:
|
||||
ContextPromotionPass();
|
||||
virtual ~ContextPromotionPass();
|
||||
virtual ~ContextPromotionPass() override;
|
||||
|
||||
virtual int Initialize(Compiler* compiler);
|
||||
int Initialize(Compiler* compiler) override;
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
private:
|
||||
void PromoteBlock(hir::Block* block);
|
||||
void RemoveDeadStoresBlock(hir::Block* block);
|
||||
|
||||
private:
|
||||
size_t context_values_size_;
|
||||
hir::Value** context_values_;
|
||||
private:
|
||||
std::vector<hir::Value*> context_values_;
|
||||
llvm::BitVector context_validity_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
||||
|
|
|
@ -7,35 +7,42 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/control_flow_analysis_pass.h>
|
||||
#include "alloy/compiler/passes/control_flow_analysis_pass.h"
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include "alloy/backend/backend.h"
|
||||
#include "alloy/compiler/compiler.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
using namespace alloy::frontend;
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
using alloy::hir::Edge;
|
||||
using alloy::hir::HIRBuilder;
|
||||
|
||||
ControlFlowAnalysisPass::ControlFlowAnalysisPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
ControlFlowAnalysisPass::ControlFlowAnalysisPass() : CompilerPass() {}
|
||||
|
||||
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {
|
||||
}
|
||||
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {}
|
||||
|
||||
int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
// TODO(benvanik): reset edges for all blocks? Needed to be re-runnable.
|
||||
// Reset edges for all blocks. Needed to be re-runnable.
|
||||
// Note that this wastes a bunch of arena memory, so we shouldn't
|
||||
// re-run too often.
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
block->incoming_edge_head = nullptr;
|
||||
block->outgoing_edge_head = nullptr;
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
// Add edges.
|
||||
auto block = builder->first_block();
|
||||
block = builder->first_block();
|
||||
while (block) {
|
||||
auto instr = block->instr_tail;
|
||||
while (instr) {
|
||||
|
@ -46,7 +53,7 @@ int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
|||
auto label = instr->src1.label;
|
||||
builder->AddEdge(block, label->block, Edge::UNCONDITIONAL);
|
||||
} else if (instr->opcode == &OPCODE_BRANCH_TRUE_info ||
|
||||
instr->opcode == &OPCODE_BRANCH_FALSE_info) {
|
||||
instr->opcode == &OPCODE_BRANCH_FALSE_info) {
|
||||
auto label = instr->src2.label;
|
||||
builder->AddEdge(block, label->block, 0);
|
||||
}
|
||||
|
@ -67,3 +74,7 @@ int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,28 +10,24 @@
|
|||
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
|
||||
class ControlFlowAnalysisPass : public CompilerPass {
|
||||
public:
|
||||
public:
|
||||
ControlFlowAnalysisPass();
|
||||
virtual ~ControlFlowAnalysisPass();
|
||||
~ControlFlowAnalysisPass() override;
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "alloy/compiler/passes/control_flow_simplification_pass.h"
|
||||
|
||||
#include "alloy/backend/backend.h"
|
||||
#include "alloy/compiler/compiler.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
|
||||
using alloy::hir::Edge;
|
||||
using alloy::hir::HIRBuilder;
|
||||
|
||||
ControlFlowSimplificationPass::ControlFlowSimplificationPass()
|
||||
: CompilerPass() {}
|
||||
|
||||
ControlFlowSimplificationPass::~ControlFlowSimplificationPass() {}
|
||||
|
||||
int ControlFlowSimplificationPass::Run(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
||||
// Walk backwards and merge blocks if possible.
|
||||
bool merged_any = false;
|
||||
auto block = builder->last_block();
|
||||
while (block) {
|
||||
auto prev_block = block->prev;
|
||||
const uint32_t expected = Edge::DOMINATES | Edge::UNCONDITIONAL;
|
||||
if (block->incoming_edge_head &&
|
||||
(block->incoming_edge_head->flags & expected) == expected) {
|
||||
// Dominated by the incoming block.
|
||||
// If that block comes immediately before us then we can merge the
|
||||
// two blocks (assuming it's not a volatile instruction like Trap).
|
||||
if (block->prev == block->incoming_edge_head->src &&
|
||||
block->prev->instr_tail &&
|
||||
!(block->prev->instr_tail->opcode->flags & OPCODE_FLAG_VOLATILE)) {
|
||||
builder->MergeAdjacentBlocks(block->prev, block);
|
||||
merged_any = true;
|
||||
}
|
||||
}
|
||||
block = prev_block;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_
|
||||
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
class ControlFlowSimplificationPass : public CompilerPass {
|
||||
public:
|
||||
ControlFlowSimplificationPass();
|
||||
~ControlFlowSimplificationPass() override;
|
||||
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
||||
#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_
|
|
@ -7,33 +7,37 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
|
||||
#include "alloy/compiler/passes/data_flow_analysis_pass.h"
|
||||
|
||||
#include <alloy/backend/backend.h>
|
||||
#include <alloy/compiler/compiler.h>
|
||||
#include <alloy/runtime/runtime.h>
|
||||
#include "alloy/backend/backend.h"
|
||||
#include "alloy/compiler/compiler.h"
|
||||
#include "alloy/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
#if XE_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4267)
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#endif // XE_COMPILER_MSVC
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
using namespace alloy::frontend;
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
using namespace alloy::runtime;
|
||||
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::hir::OpcodeSignatureType;
|
||||
using alloy::hir::Value;
|
||||
|
||||
DataFlowAnalysisPass::DataFlowAnalysisPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
DataFlowAnalysisPass::DataFlowAnalysisPass() : CompilerPass() {}
|
||||
|
||||
DataFlowAnalysisPass::~DataFlowAnalysisPass() {
|
||||
}
|
||||
DataFlowAnalysisPass::~DataFlowAnalysisPass() {}
|
||||
|
||||
int DataFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
@ -66,15 +70,15 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
|
||||
// Stash for value map. We may want to maintain this during building.
|
||||
auto arena = builder->arena();
|
||||
Value** value_map = (Value**)arena->Alloc(
|
||||
sizeof(Value*) * max_value_estimate);
|
||||
Value** value_map =
|
||||
(Value**)arena->Alloc(sizeof(Value*) * max_value_estimate);
|
||||
|
||||
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
|
||||
// because they are only used during the block iteration.
|
||||
// Mapped by block ordinal.
|
||||
// TODO(benvanik): cache this list, grow as needed, etc.
|
||||
auto incoming_bitvectors = (llvm::BitVector**)arena->Alloc(
|
||||
sizeof(llvm::BitVector*) * block_count);
|
||||
auto incoming_bitvectors =
|
||||
(llvm::BitVector**)arena->Alloc(sizeof(llvm::BitVector*) * block_count);
|
||||
for (auto n = 0u; n < block_count; n++) {
|
||||
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
|
||||
}
|
||||
|
@ -90,11 +94,11 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
auto instr = block->instr_head;
|
||||
while (instr) {
|
||||
uint32_t signature = instr->opcode->signature;
|
||||
#define SET_INCOMING_VALUE(v) \
|
||||
if (v->def && v->def->block != block) { \
|
||||
incoming_values.set(v->ordinal); \
|
||||
} \
|
||||
XEASSERT(v->ordinal < max_value_estimate); \
|
||||
#define SET_INCOMING_VALUE(v) \
|
||||
if (v->def && v->def->block != block) { \
|
||||
incoming_values.set(v->ordinal); \
|
||||
} \
|
||||
assert_true(v->ordinal < max_value_estimate); \
|
||||
value_map[v->ordinal] = v;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
SET_INCOMING_VALUE(instr->src1.value);
|
||||
|
@ -125,7 +129,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
auto outgoing_ordinal = outgoing_values.find_first();
|
||||
while (outgoing_ordinal != -1) {
|
||||
Value* src_value = value_map[outgoing_ordinal];
|
||||
XEASSERTNOTNULL(src_value);
|
||||
assert_not_null(src_value);
|
||||
if (!src_value->local_slot) {
|
||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
||||
}
|
||||
|
@ -139,7 +143,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
while (def_next && def_next->opcode->flags & OPCODE_FLAG_PAIRED_PREV) {
|
||||
def_next = def_next->next;
|
||||
}
|
||||
XEASSERTNOTNULL(def_next);
|
||||
assert_not_null(def_next);
|
||||
builder->last_instr()->MoveBefore(def_next);
|
||||
|
||||
// We don't need it in the incoming list.
|
||||
|
@ -150,7 +154,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
while (tail && tail->opcode->flags & OPCODE_FLAG_BRANCH) {
|
||||
tail = tail->prev;
|
||||
}
|
||||
XEASSERTNOTZERO(tail);
|
||||
assert_not_zero(tail);
|
||||
builder->last_instr()->MoveBefore(tail->next);
|
||||
}
|
||||
|
||||
|
@ -161,7 +165,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
auto incoming_ordinal = incoming_values.find_first();
|
||||
while (incoming_ordinal != -1) {
|
||||
Value* src_value = value_map[incoming_ordinal];
|
||||
XEASSERTNOTNULL(src_value);
|
||||
assert_not_null(src_value);
|
||||
if (!src_value->local_slot) {
|
||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
||||
}
|
||||
|
@ -169,7 +173,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
builder->last_instr()->MoveBefore(block->instr_head);
|
||||
|
||||
// Swap uses of original value with the local value.
|
||||
auto instr = block->instr_head;
|
||||
instr = block->instr_head;
|
||||
while (instr) {
|
||||
uint32_t signature = instr->opcode->signature;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
|
@ -201,3 +205,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
|||
delete incoming_bitvectors[n];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
|
||||
namespace alloy {
|
||||
|
@ -21,9 +21,9 @@ namespace passes {
|
|||
class DataFlowAnalysisPass : public CompilerPass {
|
||||
public:
|
||||
DataFlowAnalysisPass();
|
||||
virtual ~DataFlowAnalysisPass();
|
||||
~DataFlowAnalysisPass() override;
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
uint32_t LinearizeBlocks(hir::HIRBuilder* builder);
|
||||
|
|
|
@ -7,20 +7,24 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
||||
#include "alloy/compiler/passes/dead_code_elimination_pass.h"
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::compiler;
|
||||
using namespace alloy::compiler::passes;
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace alloy {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace alloy::hir;
|
||||
|
||||
using alloy::hir::HIRBuilder;
|
||||
using alloy::hir::Instr;
|
||||
using alloy::hir::Value;
|
||||
|
||||
DeadCodeEliminationPass::DeadCodeEliminationPass() :
|
||||
CompilerPass() {
|
||||
}
|
||||
DeadCodeEliminationPass::DeadCodeEliminationPass() : CompilerPass() {}
|
||||
|
||||
DeadCodeEliminationPass::~DeadCodeEliminationPass() {
|
||||
}
|
||||
DeadCodeEliminationPass::~DeadCodeEliminationPass() {}
|
||||
|
||||
int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||
SCOPE_profile_cpu_f("alloy");
|
||||
|
@ -63,7 +67,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
|||
|
||||
bool any_instr_removed = false;
|
||||
bool any_locals_removed = false;
|
||||
Block* block = builder->first_block();
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
// Walk instructions in reverse.
|
||||
Instr* i = block->instr_tail;
|
||||
|
@ -71,8 +75,8 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
|||
auto prev = i->prev;
|
||||
|
||||
auto opcode = i->opcode;
|
||||
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) &&
|
||||
i->dest && !i->dest->use_head) {
|
||||
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) && i->dest &&
|
||||
!i->dest->use_head) {
|
||||
// Has no uses and is not volatile. This instruction can die!
|
||||
MakeNopRecursive(i);
|
||||
any_instr_removed = true;
|
||||
|
@ -110,7 +114,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
|||
|
||||
// Remove all nops.
|
||||
if (any_instr_removed) {
|
||||
Block* block = builder->first_block();
|
||||
block = builder->first_block();
|
||||
while (block) {
|
||||
Instr* i = block->instr_head;
|
||||
while (i) {
|
||||
|
@ -148,19 +152,19 @@ void DeadCodeEliminationPass::MakeNopRecursive(Instr* i) {
|
|||
i->dest->def = NULL;
|
||||
i->dest = NULL;
|
||||
|
||||
#define MAKE_NOP_SRC(n) \
|
||||
if (i->src##n##_use) { \
|
||||
Value::Use* use = i->src##n##_use; \
|
||||
Value* value = i->src##n##.value; \
|
||||
i->src##n##_use = NULL; \
|
||||
i->src##n##.value = NULL; \
|
||||
value->RemoveUse(use); \
|
||||
if (!value->use_head) { \
|
||||
#define MAKE_NOP_SRC(n) \
|
||||
if (i->src##n##_use) { \
|
||||
Value::Use* use = i->src##n##_use; \
|
||||
Value* value = i->src##n.value; \
|
||||
i->src##n##_use = NULL; \
|
||||
i->src##n.value = NULL; \
|
||||
value->RemoveUse(use); \
|
||||
if (!value->use_head) { \
|
||||
/* Value is now unused, so recursively kill it. */ \
|
||||
if (value->def && value->def != i) { \
|
||||
MakeNopRecursive(value->def); \
|
||||
} \
|
||||
} \
|
||||
if (value->def && value->def != i) { \
|
||||
MakeNopRecursive(value->def); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
MAKE_NOP_SRC(1);
|
||||
MAKE_NOP_SRC(2);
|
||||
|
@ -190,7 +194,6 @@ void DeadCodeEliminationPass::ReplaceAssignment(Instr* i) {
|
|||
}
|
||||
|
||||
bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
||||
auto slot = i->src1.value;
|
||||
auto src = i->src2.value;
|
||||
|
||||
auto use = src->use_head;
|
||||
|
@ -209,3 +212,7 @@ bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace alloy
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
|
||||
#define ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
|
||||
|
||||
#include <alloy/compiler/compiler_pass.h>
|
||||
#include "alloy/compiler/compiler_pass.h"
|
||||
|
||||
|
||||
namespace alloy {
|
||||
|
@ -21,9 +21,9 @@ namespace passes {
|
|||
class DeadCodeEliminationPass : public CompilerPass {
|
||||
public:
|
||||
DeadCodeEliminationPass();
|
||||
virtual ~DeadCodeEliminationPass();
|
||||
~DeadCodeEliminationPass() override;
|
||||
|
||||
virtual int Run(hir::HIRBuilder* builder);
|
||||
int Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
private:
|
||||
void MakeNopRecursive(hir::Instr* i);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue