Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
19ca4796be
|
@ -42,6 +42,7 @@ tmtags
|
||||||
|
|
||||||
*.py[co]
|
*.py[co]
|
||||||
.coverage
|
.coverage
|
||||||
|
*.o
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Logs and dumps
|
# Logs and dumps
|
||||||
|
@ -49,6 +50,7 @@ tmtags
|
||||||
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
private/
|
private/
|
||||||
|
*.trace
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Build system output
|
# Build system output
|
||||||
|
@ -70,3 +72,16 @@ build-out/
|
||||||
build-gen/
|
build-gen/
|
||||||
build-bin/
|
build-bin/
|
||||||
build-test/
|
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"]
|
[submodule "third_party/gflags"]
|
||||||
path = third_party/gflags
|
path = third_party/gflags
|
||||||
url = https://github.com/benvanik/gflags.git
|
url = https://github.com/benvanik/gflags.git
|
||||||
|
@ -13,12 +7,15 @@
|
||||||
[submodule "third_party/beaengine"]
|
[submodule "third_party/beaengine"]
|
||||||
path = third_party/beaengine
|
path = third_party/beaengine
|
||||||
url = https://github.com/benvanik/beaengine.git
|
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"]
|
[submodule "third_party/xbyak"]
|
||||||
path = third_party/xbyak
|
path = third_party/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
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
|
## 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!
|
* Asserts! Crashes! Hangs! Blank screens!
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
@ -35,7 +35,7 @@ legally purchased devices and games and information made public on the internet
|
||||||
|
|
||||||
Windows 8.1+:
|
Windows 8.1+:
|
||||||
|
|
||||||
# install python 2.7 and VS2013
|
# install python 2.7 and VS2013/2015
|
||||||
git clone https://github.com/benvanik/xenia.git
|
git clone https://github.com/benvanik/xenia.git
|
||||||
cd xenia
|
cd xenia
|
||||||
xb setup
|
xb setup
|
||||||
|
@ -47,7 +47,8 @@ update gyp files/etc.
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
See [building](docs/building.md) for setup and information about the
|
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!
|
## 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
|
Contribute! There's a ton of work that needs to be done, a lot of which
|
||||||
is wide open greenfield fun.
|
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
|
Fixes and optimizations are always welcome (please!), but in addition to
|
||||||
that there are some major work areas still untouched:
|
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)
|
* 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 [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
|
||||||
* 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)
|
|
||||||
|
|
||||||
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
|
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.
|
on something.
|
||||||
|
@ -91,15 +84,13 @@ enough to run games at a decent speed the answer is no.
|
||||||
### What about Linux/OSX?
|
### What about Linux/OSX?
|
||||||
|
|
||||||
The project is designed to support non-Windows platforms but until it's running
|
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
|
games it's not worth the maintenance burden. OSX will likely remain unsupported
|
||||||
Linux/OSX-based developer and want to help out, run Bootcamp/VM and contribute
|
until Apple supports OpenGL 4.3 or higher.
|
||||||
an OpenGL 4 driver - that'll be the most difficult part in porting to
|
|
||||||
non-Windows platforms.
|
|
||||||
|
|
||||||
### What kind of GPU do I need?
|
### What kind of GPU do I need?
|
||||||
|
|
||||||
DirectX 11 support is required. To get full speed and compatibility Mantle may
|
OpenGL 4.5 support and drivers are required. To get full speed and compatibility
|
||||||
be required in the future.
|
GL-next may eventually be required.
|
||||||
|
|
||||||
### Have you heard of LLVM/asmjit/jitasm/luajit/etc?
|
### 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
|
||||||
|
|
||||||
* Windows 8 or 8.1
|
* Windows 8 or 8.1
|
||||||
* Visual Studio 2013
|
* Visual Studio 2013+
|
||||||
* [Python 2.7](http://www.python.org/download/releases/2.7.6/)
|
* [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)
|
* 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\`).
|
Ensure Python is in your PATH (`C:\Python27\`).
|
||||||
|
|
||||||
|
I recommend using [Cmder](http://bliker.github.io/cmder/) for git and command
|
||||||
|
line usage.
|
||||||
|
|
||||||
#### Debugging
|
#### 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
|
and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
|
||||||
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
|
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
|
||||||
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
|
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
|
## xenia-build
|
||||||
|
|
||||||
A simple build script is included to manage basic tasks such as building
|
A simple build script is included to manage basic tasks such as building
|
||||||
|
@ -44,7 +58,7 @@ keypresses:
|
||||||
|
|
||||||
#### setup
|
#### 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
|
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
|
To make life easier you can use `--flagfile=myflags.txt` to specify all
|
||||||
arguments, including using `--target=my.xex` to pick an executable.
|
arguments, including using `--target=my.xex` to pick an executable.
|
||||||
|
|
||||||
### xenia-info
|
### xenia
|
||||||
|
|
||||||
Dumps information about a xex file.
|
|
||||||
|
|
||||||
./bin/xenia-info some.xex
|
|
||||||
|
|
||||||
### xenia-run
|
|
||||||
|
|
||||||
Runs a xex.
|
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_
|
#ifndef ALLOY_ALLOY_PRIVATE_H_
|
||||||
#define ALLOY_ALLOY_PRIVATE_H_
|
#define ALLOY_ALLOY_PRIVATE_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
|
||||||
DECLARE_bool(debug);
|
DECLARE_bool(debug);
|
||||||
DECLARE_bool(always_disasm);
|
DECLARE_bool(always_disasm);
|
||||||
|
|
||||||
|
@ -22,11 +19,6 @@ DECLARE_bool(validate_hir);
|
||||||
|
|
||||||
DECLARE_uint64(break_on_instruction);
|
DECLARE_uint64(break_on_instruction);
|
||||||
DECLARE_uint64(break_on_memory);
|
DECLARE_uint64(break_on_memory);
|
||||||
|
DECLARE_bool(break_on_debugbreak);
|
||||||
|
|
||||||
namespace alloy {
|
|
||||||
|
|
||||||
} // namespace alloy
|
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_ALLOY_PRIVATE_H_
|
#endif // ALLOY_ALLOY_PRIVATE_H_
|
||||||
|
|
|
@ -7,12 +7,11 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <alloy/alloy.h>
|
#include "alloy/alloy.h"
|
||||||
#include <alloy/alloy-private.h>
|
#include "alloy/alloy-private.h"
|
||||||
|
|
||||||
using namespace alloy;
|
using namespace alloy;
|
||||||
|
|
||||||
|
|
||||||
#if 0 && DEBUG
|
#if 0 && DEBUG
|
||||||
#define DEFAULT_DEBUG_FLAG true
|
#define DEFAULT_DEBUG_FLAG true
|
||||||
#else
|
#else
|
||||||
|
@ -21,7 +20,8 @@ using namespace alloy;
|
||||||
|
|
||||||
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
|
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
|
||||||
"Allow debugging and retain debug information.");
|
"Allow debugging and retain debug information.");
|
||||||
DEFINE_bool(always_disasm, false,
|
DEFINE_bool(
|
||||||
|
always_disasm, false,
|
||||||
"Always add debug info to functions, even when no debugger is attached.");
|
"Always add debug info to functions, even when no debugger is attached.");
|
||||||
|
|
||||||
DEFINE_bool(validate_hir, false,
|
DEFINE_bool(validate_hir, false,
|
||||||
|
@ -32,3 +32,4 @@ 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,
|
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_
|
#ifndef ALLOY_ALLOY_H_
|
||||||
#define ALLOY_ALLOY_H_
|
#define ALLOY_ALLOY_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include "alloy/runtime/function.h"
|
||||||
|
#include "alloy/runtime/module.h"
|
||||||
#include <alloy/runtime/function.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
#include <alloy/runtime/module.h>
|
#include "alloy/runtime/thread_state.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
|
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_ALLOY_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) :
|
Arena::Arena(size_t chunk_size)
|
||||||
chunk_size_(chunk_size),
|
: chunk_size_(chunk_size), head_chunk_(nullptr), active_chunk_(nullptr) {}
|
||||||
head_chunk_(NULL), active_chunk_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Arena::~Arena() {
|
Arena::~Arena() {
|
||||||
Reset();
|
Reset();
|
||||||
|
@ -25,7 +24,7 @@ Arena::~Arena() {
|
||||||
delete chunk;
|
delete chunk;
|
||||||
chunk = next;
|
chunk = next;
|
||||||
}
|
}
|
||||||
head_chunk_ = NULL;
|
head_chunk_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arena::Reset() {
|
void Arena::Reset() {
|
||||||
|
@ -48,7 +47,7 @@ void* Arena::Alloc(size_t size) {
|
||||||
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
|
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
|
||||||
Chunk* next = active_chunk_->next;
|
Chunk* next = active_chunk_->next;
|
||||||
if (!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_);
|
next = new Chunk(chunk_size_);
|
||||||
active_chunk_->next = next;
|
active_chunk_->next = next;
|
||||||
}
|
}
|
||||||
|
@ -74,11 +73,11 @@ void* Arena::CloneContents() {
|
||||||
}
|
}
|
||||||
chunk = chunk->next;
|
chunk = chunk->next;
|
||||||
}
|
}
|
||||||
void* result = xe_malloc(total_length);
|
void* result = malloc(total_length);
|
||||||
uint8_t* p = (uint8_t*)result;
|
uint8_t* p = (uint8_t*)result;
|
||||||
chunk = head_chunk_;
|
chunk = head_chunk_;
|
||||||
while (chunk) {
|
while (chunk) {
|
||||||
xe_copy_struct(p, chunk->buffer, chunk->offset);
|
memcpy(p, chunk->buffer, chunk->offset);
|
||||||
p += chunk->offset;
|
p += chunk->offset;
|
||||||
if (chunk == active_chunk_) {
|
if (chunk == active_chunk_) {
|
||||||
break;
|
break;
|
||||||
|
@ -88,14 +87,15 @@ void* Arena::CloneContents() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena::Chunk::Chunk(size_t chunk_size) :
|
Arena::Chunk::Chunk(size_t chunk_size)
|
||||||
next(NULL),
|
: next(nullptr), capacity(chunk_size), buffer(0), offset(0) {
|
||||||
capacity(chunk_size), buffer(0), offset(0) {
|
buffer = reinterpret_cast<uint8_t*>(malloc(capacity));
|
||||||
buffer = (uint8_t*)xe_malloc(capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena::Chunk::~Chunk() {
|
Arena::Chunk::~Chunk() {
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
xe_free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,12 +10,11 @@
|
||||||
#ifndef ALLOY_ARENA_H_
|
#ifndef ALLOY_ARENA_H_
|
||||||
#define ALLOY_ARENA_H_
|
#define ALLOY_ARENA_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
|
|
||||||
|
|
||||||
class Arena {
|
class Arena {
|
||||||
public:
|
public:
|
||||||
Arena(size_t chunk_size = 4 * 1024 * 1024);
|
Arena(size_t chunk_size = 4 * 1024 * 1024);
|
||||||
|
@ -25,8 +24,9 @@ public:
|
||||||
void DebugFill();
|
void DebugFill();
|
||||||
|
|
||||||
void* Alloc(size_t size);
|
void* Alloc(size_t size);
|
||||||
template<typename T> T* Alloc() {
|
template <typename T>
|
||||||
return (T*)Alloc(sizeof(T));
|
T* Alloc() {
|
||||||
|
return reinterpret_cast<T*>(Alloc(sizeof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void* CloneContents();
|
void* CloneContents();
|
||||||
|
@ -50,8 +50,6 @@ private:
|
||||||
Chunk* active_chunk_;
|
Chunk* active_chunk_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_ARENA_H_
|
#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;
|
Assembler::Assembler(Backend* backend) : backend_(backend) {}
|
||||||
using namespace alloy::backend;
|
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
Assembler::~Assembler() { Reset(); }
|
||||||
|
|
||||||
Assembler::Assembler(Backend* backend) :
|
int Assembler::Initialize() { return 0; }
|
||||||
backend_(backend) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Assembler::~Assembler() {
|
void Assembler::Reset() {}
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Assembler::Initialize() {
|
} // namespace backend
|
||||||
return 0;
|
} // namespace alloy
|
||||||
}
|
|
||||||
|
|
||||||
void Assembler::Reset() {
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,27 +10,25 @@
|
||||||
#ifndef ALLOY_BACKEND_ASSEMBLER_H_
|
#ifndef ALLOY_BACKEND_ASSEMBLER_H_
|
||||||
#define ALLOY_BACKEND_ASSEMBLER_H_
|
#define ALLOY_BACKEND_ASSEMBLER_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace hir {
|
namespace hir {
|
||||||
class HIRBuilder;
|
class HIRBuilder;
|
||||||
}
|
} // namespace hir
|
||||||
namespace runtime {
|
namespace runtime {
|
||||||
class DebugInfo;
|
class DebugInfo;
|
||||||
class Function;
|
class Function;
|
||||||
class FunctionInfo;
|
class FunctionInfo;
|
||||||
class Runtime;
|
class Runtime;
|
||||||
}
|
} // namespace runtime
|
||||||
}
|
} // namespace alloy
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
|
||||||
class Backend;
|
class Backend;
|
||||||
|
|
||||||
|
|
||||||
class Assembler {
|
class Assembler {
|
||||||
public:
|
public:
|
||||||
Assembler(Backend* backend);
|
Assembler(Backend* backend);
|
||||||
|
@ -40,18 +38,17 @@ public:
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual int Assemble(
|
virtual int Assemble(runtime::FunctionInfo* symbol_info,
|
||||||
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||||
|
uint32_t trace_flags,
|
||||||
runtime::Function** out_function) = 0;
|
runtime::Function** out_function) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Backend* backend_;
|
Backend* backend_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_ASSEMBLER_H_
|
#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 alloy::runtime::Runtime;
|
||||||
using namespace alloy::backend;
|
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
Backend::Backend(Runtime* runtime) : runtime_(runtime) {
|
||||||
Backend::Backend(Runtime* runtime) :
|
memset(&machine_info_, 0, sizeof(machine_info_));
|
||||||
runtime_(runtime) {
|
|
||||||
xe_zero_struct(&machine_info_, sizeof(machine_info_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Backend::~Backend() {
|
Backend::~Backend() = default;
|
||||||
}
|
|
||||||
|
|
||||||
int Backend::Initialize() {
|
int Backend::Initialize() { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* Backend::AllocThreadData() {
|
void* Backend::AllocThreadData() { return nullptr; }
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Backend::FreeThreadData(void* thread_data) {
|
void Backend::FreeThreadData(void* thread_data) {}
|
||||||
}
|
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,18 +10,21 @@
|
||||||
#ifndef ALLOY_BACKEND_BACKEND_H_
|
#ifndef ALLOY_BACKEND_BACKEND_H_
|
||||||
#define ALLOY_BACKEND_BACKEND_H_
|
#define ALLOY_BACKEND_BACKEND_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <memory>
|
||||||
#include <alloy/backend/machine_info.h>
|
|
||||||
|
|
||||||
|
#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 alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
|
||||||
class Assembler;
|
class Assembler;
|
||||||
|
|
||||||
|
|
||||||
class Backend {
|
class Backend {
|
||||||
public:
|
public:
|
||||||
Backend(runtime::Runtime* runtime);
|
Backend(runtime::Runtime* runtime);
|
||||||
|
@ -35,16 +38,14 @@ public:
|
||||||
virtual void* AllocThreadData();
|
virtual void* AllocThreadData();
|
||||||
virtual void FreeThreadData(void* thread_data);
|
virtual void FreeThreadData(void* thread_data);
|
||||||
|
|
||||||
virtual Assembler* CreateAssembler() = 0;
|
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
runtime::Runtime* runtime_;
|
runtime::Runtime* runtime_;
|
||||||
MachineInfo machine_info_;
|
MachineInfo machine_info_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_BACKEND_H_
|
#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_
|
#ifndef ALLOY_BACKEND_MACHINE_INFO_H_
|
||||||
#define ALLOY_BACKEND_MACHINE_INFO_H_
|
#define ALLOY_BACKEND_MACHINE_INFO_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
|
||||||
|
|
||||||
struct MachineInfo {
|
struct MachineInfo {
|
||||||
struct RegisterSet {
|
struct RegisterSet {
|
||||||
enum Types {
|
enum Types {
|
||||||
|
@ -31,9 +29,7 @@ struct MachineInfo {
|
||||||
} register_sets[8];
|
} register_sets[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_MACHINE_INFO_H_
|
#endif // ALLOY_BACKEND_MACHINE_INFO_H_
|
||||||
|
|
|
@ -6,11 +6,9 @@
|
||||||
'backend.cc',
|
'backend.cc',
|
||||||
'backend.h',
|
'backend.h',
|
||||||
'machine_info.h',
|
'machine_info.h',
|
||||||
'tracing.h',
|
|
||||||
],
|
],
|
||||||
|
|
||||||
'includes': [
|
'includes': [
|
||||||
'ivm/sources.gypi',
|
|
||||||
'x64/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.
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
'tracing.h',
|
|
||||||
'x64_assembler.cc',
|
'x64_assembler.cc',
|
||||||
'x64_assembler.h',
|
'x64_assembler.h',
|
||||||
'x64_backend.cc',
|
'x64_backend.cc',
|
||||||
'x64_backend.h',
|
'x64_backend.h',
|
||||||
'x64_code_cache.cc',
|
|
||||||
'x64_code_cache.h',
|
'x64_code_cache.h',
|
||||||
'x64_emitter.cc',
|
'x64_emitter.cc',
|
||||||
'x64_emitter.h',
|
'x64_emitter.h',
|
||||||
|
@ -20,4 +18,17 @@
|
||||||
'x64_tracers.cc',
|
'x64_tracers.cc',
|
||||||
'x64_tracers.h',
|
'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/reset_scope.h"
|
||||||
#include <alloy/backend/x64/x64_backend.h>
|
#include "alloy/backend/x64/x64_backend.h"
|
||||||
#include <alloy/backend/x64/x64_emitter.h>
|
#include "alloy/backend/x64/x64_emitter.h"
|
||||||
#include <alloy/backend/x64/x64_function.h>
|
#include "alloy/backend/x64/x64_function.h"
|
||||||
#include <alloy/hir/hir_builder.h>
|
#include "alloy/hir/hir_builder.h"
|
||||||
#include <alloy/hir/label.h>
|
#include "alloy/hir/label.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
namespace BE {
|
namespace BE {
|
||||||
#include <beaengine/BeaEngine.h>
|
#include <beaengine/BeaEngine.h>
|
||||||
}
|
} // namespace BE
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::backend;
|
namespace backend {
|
||||||
using namespace alloy::backend::x64;
|
namespace x64 {
|
||||||
using namespace alloy::hir;
|
|
||||||
|
// TODO(benvanik): remove when enums redefined.
|
||||||
using namespace alloy::runtime;
|
using namespace alloy::runtime;
|
||||||
|
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
using alloy::runtime::DebugInfo;
|
||||||
|
using alloy::runtime::Function;
|
||||||
|
using alloy::runtime::FunctionInfo;
|
||||||
|
|
||||||
X64Assembler::X64Assembler(X64Backend* backend) :
|
X64Assembler::X64Assembler(X64Backend* backend)
|
||||||
x64_backend_(backend),
|
: Assembler(backend), x64_backend_(backend) {}
|
||||||
emitter_(0), allocator_(0),
|
|
||||||
Assembler(backend) {
|
|
||||||
}
|
|
||||||
|
|
||||||
X64Assembler::~X64Assembler() {
|
X64Assembler::~X64Assembler() {
|
||||||
alloy::tracing::WriteEvent(EventType::AssemblerDeinit({
|
// Emitter must be freed before the allocator.
|
||||||
}));
|
emitter_.reset();
|
||||||
|
allocator_.reset();
|
||||||
delete emitter_;
|
|
||||||
delete allocator_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64Assembler::Initialize() {
|
int X64Assembler::Initialize() {
|
||||||
|
@ -48,11 +49,8 @@ int X64Assembler::Initialize() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
allocator_ = new XbyakAllocator();
|
allocator_.reset(new XbyakAllocator());
|
||||||
emitter_ = new X64Emitter(x64_backend_, allocator_);
|
emitter_.reset(new X64Emitter(x64_backend_, allocator_.get()));
|
||||||
|
|
||||||
alloy::tracing::WriteEvent(EventType::AssemblerInit({
|
|
||||||
}));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -62,50 +60,45 @@ void X64Assembler::Reset() {
|
||||||
Assembler::Reset();
|
Assembler::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64Assembler::Assemble(
|
int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||||
FunctionInfo* symbol_info, HIRBuilder* builder,
|
uint32_t debug_info_flags,
|
||||||
uint32_t debug_info_flags, DebugInfo* debug_info,
|
std::unique_ptr<DebugInfo> debug_info,
|
||||||
Function** out_function) {
|
uint32_t trace_flags, Function** out_function) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
int result = 0;
|
// Reset when we leave.
|
||||||
|
make_reset_scope(this);
|
||||||
|
|
||||||
// Lower HIR -> x64.
|
// Lower HIR -> x64.
|
||||||
void* machine_code = 0;
|
void* machine_code = 0;
|
||||||
size_t code_size = 0;
|
size_t code_size = 0;
|
||||||
result = emitter_->Emit(builder,
|
int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(),
|
||||||
debug_info_flags, debug_info,
|
trace_flags, machine_code, code_size);
|
||||||
machine_code, code_size);
|
if (result) {
|
||||||
XEEXPECTZERO(result);
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Stash generated machine code.
|
// Stash generated machine code.
|
||||||
if (debug_info_flags & DEBUG_INFO_MACHINE_CODE_DISASM) {
|
if (debug_info_flags & DebugInfoFlags::DEBUG_INFO_MACHINE_CODE_DISASM) {
|
||||||
DumpMachineCode(debug_info, machine_code, code_size, &string_buffer_);
|
DumpMachineCode(debug_info.get(), machine_code, code_size, &string_buffer_);
|
||||||
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
debug_info->set_machine_code_disasm(string_buffer_.ToString());
|
||||||
string_buffer_.Reset();
|
string_buffer_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
X64Function* fn = new X64Function(symbol_info);
|
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);
|
fn->Setup(machine_code, code_size);
|
||||||
|
|
||||||
*out_function = fn;
|
*out_function = fn;
|
||||||
|
|
||||||
result = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XECLEANUP:
|
return 0;
|
||||||
Reset();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Assembler::DumpMachineCode(
|
void X64Assembler::DumpMachineCode(DebugInfo* debug_info, void* machine_code,
|
||||||
DebugInfo* debug_info,
|
size_t code_size, StringBuffer* str) {
|
||||||
void* machine_code, size_t code_size,
|
BE::DISASM disasm = {0};
|
||||||
StringBuffer* str) {
|
|
||||||
BE::DISASM disasm;
|
|
||||||
xe_zero_struct(&disasm, sizeof(disasm));
|
|
||||||
disasm.Archi = 64;
|
disasm.Archi = 64;
|
||||||
disasm.Options = BE::Tabulation + BE::MasmSyntax + BE::PrefixedNumeral;
|
disasm.Options = BE::Tabulation + BE::MasmSyntax + BE::PrefixedNumeral;
|
||||||
disasm.EIP = (BE::UIntPtr)machine_code;
|
disasm.EIP = (BE::UIntPtr)machine_code;
|
||||||
|
@ -113,8 +106,8 @@ void X64Assembler::DumpMachineCode(
|
||||||
uint64_t prev_source_offset = 0;
|
uint64_t prev_source_offset = 0;
|
||||||
while (disasm.EIP < eip_end) {
|
while (disasm.EIP < eip_end) {
|
||||||
// Look up source offset.
|
// Look up source offset.
|
||||||
auto map_entry = debug_info->LookupCodeOffset(
|
auto map_entry =
|
||||||
disasm.EIP - (BE::UIntPtr)machine_code);
|
debug_info->LookupCodeOffset(disasm.EIP - (BE::UIntPtr)machine_code);
|
||||||
if (map_entry) {
|
if (map_entry) {
|
||||||
if (map_entry->source_offset == prev_source_offset) {
|
if (map_entry->source_offset == prev_source_offset) {
|
||||||
str->Append(" ");
|
str->Append(" ");
|
||||||
|
@ -134,3 +127,7 @@ void X64Assembler::DumpMachineCode(
|
||||||
disasm.EIP += len;
|
disasm.EIP += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace x64
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
#ifndef ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
||||||
#define ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
#define ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <memory>
|
||||||
|
|
||||||
#include <alloy/backend/assembler.h>
|
|
||||||
|
|
||||||
|
#include "alloy/backend/assembler.h"
|
||||||
|
#include "alloy/string_buffer.h"
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -23,38 +23,34 @@ class X64Backend;
|
||||||
class X64Emitter;
|
class X64Emitter;
|
||||||
class XbyakAllocator;
|
class XbyakAllocator;
|
||||||
|
|
||||||
|
|
||||||
class X64Assembler : public Assembler {
|
class X64Assembler : public Assembler {
|
||||||
public:
|
public:
|
||||||
X64Assembler(X64Backend* backend);
|
X64Assembler(X64Backend* backend);
|
||||||
virtual ~X64Assembler();
|
~X64Assembler() override;
|
||||||
|
|
||||||
virtual int Initialize();
|
int Initialize() override;
|
||||||
|
|
||||||
virtual void Reset();
|
void Reset() override;
|
||||||
|
|
||||||
virtual int Assemble(
|
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||||
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
uint32_t debug_info_flags,
|
||||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||||
runtime::Function** out_function);
|
uint32_t trace_flags, runtime::Function** out_function) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DumpMachineCode(runtime::DebugInfo* debug_info,
|
void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code,
|
||||||
void* machine_code, size_t code_size,
|
size_t code_size, StringBuffer* str);
|
||||||
StringBuffer* str);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
X64Backend* x64_backend_;
|
X64Backend* x64_backend_;
|
||||||
X64Emitter* emitter_;
|
std::unique_ptr<X64Emitter> emitter_;
|
||||||
XbyakAllocator* allocator_;
|
std::unique_ptr<XbyakAllocator> allocator_;
|
||||||
|
|
||||||
StringBuffer string_buffer_;
|
StringBuffer string_buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
|
#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_assembler.h>
|
#include "alloy/backend/x64/x64_code_cache.h"
|
||||||
#include <alloy/backend/x64/x64_code_cache.h>
|
#include "alloy/backend/x64/x64_sequences.h"
|
||||||
#include <alloy/backend/x64/x64_sequences.h>
|
#include "alloy/backend/x64/x64_thunk_emitter.h"
|
||||||
#include <alloy/backend/x64/x64_thunk_emitter.h>
|
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::backend;
|
namespace backend {
|
||||||
using namespace alloy::backend::x64;
|
namespace x64 {
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
using alloy::runtime::Runtime;
|
||||||
|
|
||||||
X64Backend::X64Backend(Runtime* runtime) :
|
X64Backend::X64Backend(Runtime* runtime) : Backend(runtime), code_cache_(0) {}
|
||||||
code_cache_(0),
|
|
||||||
Backend(runtime) {
|
|
||||||
}
|
|
||||||
|
|
||||||
X64Backend::~X64Backend() {
|
X64Backend::~X64Backend() { delete code_cache_; }
|
||||||
alloy::tracing::WriteEvent(EventType::Deinit({
|
|
||||||
}));
|
|
||||||
delete code_cache_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int X64Backend::Initialize() {
|
int X64Backend::Initialize() {
|
||||||
int result = Backend::Initialize();
|
int result = Backend::Initialize();
|
||||||
|
@ -41,15 +33,10 @@ int X64Backend::Initialize() {
|
||||||
RegisterSequences();
|
RegisterSequences();
|
||||||
|
|
||||||
machine_info_.register_sets[0] = {
|
machine_info_.register_sets[0] = {
|
||||||
0,
|
0, "gpr", MachineInfo::RegisterSet::INT_TYPES, X64Emitter::GPR_COUNT,
|
||||||
"gpr",
|
|
||||||
MachineInfo::RegisterSet::INT_TYPES,
|
|
||||||
X64Emitter::GPR_COUNT,
|
|
||||||
};
|
};
|
||||||
machine_info_.register_sets[1] = {
|
machine_info_.register_sets[1] = {
|
||||||
1,
|
1, "xmm", MachineInfo::RegisterSet::FLOAT_TYPES |
|
||||||
"xmm",
|
|
||||||
MachineInfo::RegisterSet::FLOAT_TYPES |
|
|
||||||
MachineInfo::RegisterSet::VEC_TYPES,
|
MachineInfo::RegisterSet::VEC_TYPES,
|
||||||
X64Emitter::XMM_COUNT,
|
X64Emitter::XMM_COUNT,
|
||||||
};
|
};
|
||||||
|
@ -60,19 +47,19 @@ int X64Backend::Initialize() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto allocator = new XbyakAllocator();
|
// Generate thunks used to transition between jitted code and host code.
|
||||||
auto thunk_emitter = new X64ThunkEmitter(this, allocator);
|
auto allocator = std::make_unique<XbyakAllocator>();
|
||||||
|
auto thunk_emitter = std::make_unique<X64ThunkEmitter>(this, allocator.get());
|
||||||
host_to_guest_thunk_ = thunk_emitter->EmitHostToGuestThunk();
|
host_to_guest_thunk_ = thunk_emitter->EmitHostToGuestThunk();
|
||||||
guest_to_host_thunk_ = thunk_emitter->EmitGuestToHostThunk();
|
guest_to_host_thunk_ = thunk_emitter->EmitGuestToHostThunk();
|
||||||
delete thunk_emitter;
|
|
||||||
delete allocator;
|
|
||||||
|
|
||||||
alloy::tracing::WriteEvent(EventType::Init({
|
|
||||||
}));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assembler* X64Backend::CreateAssembler() {
|
std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
|
||||||
return new X64Assembler(this);
|
return std::make_unique<X64Assembler>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace x64
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,10 +10,7 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_BACKEND_H_
|
#ifndef ALLOY_BACKEND_X64_X64_BACKEND_H_
|
||||||
#define 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 alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -21,25 +18,23 @@ namespace x64 {
|
||||||
|
|
||||||
class X64CodeCache;
|
class X64CodeCache;
|
||||||
|
|
||||||
|
|
||||||
#define ALLOY_HAS_X64_BACKEND 1
|
#define ALLOY_HAS_X64_BACKEND 1
|
||||||
|
|
||||||
|
|
||||||
typedef void* (*HostToGuestThunk)(void* target, void* arg0, void* arg1);
|
typedef void* (*HostToGuestThunk)(void* target, void* arg0, void* arg1);
|
||||||
typedef void* (*GuestToHostThunk)(void* target, void* arg0, void* arg1);
|
typedef void* (*GuestToHostThunk)(void* target, void* arg0, void* arg1);
|
||||||
|
|
||||||
class X64Backend : public Backend {
|
class X64Backend : public Backend {
|
||||||
public:
|
public:
|
||||||
X64Backend(runtime::Runtime* runtime);
|
X64Backend(runtime::Runtime* runtime);
|
||||||
virtual ~X64Backend();
|
~X64Backend() override;
|
||||||
|
|
||||||
X64CodeCache* code_cache() const { return code_cache_; }
|
X64CodeCache* code_cache() const { return code_cache_; }
|
||||||
HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; }
|
HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; }
|
||||||
GuestToHostThunk guest_to_host_thunk() const { return guest_to_host_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_;
|
X64CodeCache* code_cache_;
|
||||||
|
@ -47,10 +42,8 @@ private:
|
||||||
GuestToHostThunk guest_to_host_thunk_;
|
GuestToHostThunk guest_to_host_thunk_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_BACKEND_H_
|
#endif // ALLOY_BACKEND_X64_X64_BACKEND_H_
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
#ifndef ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
||||||
#define ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
#define ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -34,16 +33,14 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024;
|
const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024;
|
||||||
Mutex* lock_;
|
std::mutex lock_;
|
||||||
size_t chunk_size_;
|
size_t chunk_size_;
|
||||||
X64CodeChunk* head_chunk_;
|
X64CodeChunk* head_chunk_;
|
||||||
X64CodeChunk* active_chunk_;
|
X64CodeChunk* active_chunk_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
|
#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,14 +7,10 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <alloy/backend/x64/x64_code_cache.h>
|
#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 "poly/poly.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -24,6 +20,7 @@ class X64CodeChunk {
|
||||||
public:
|
public:
|
||||||
X64CodeChunk(size_t chunk_size);
|
X64CodeChunk(size_t chunk_size);
|
||||||
~X64CodeChunk();
|
~X64CodeChunk();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
X64CodeChunk* next;
|
X64CodeChunk* next;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
|
@ -44,20 +41,11 @@ public:
|
||||||
void AddTableEntry(uint8_t* code, size_t code_size, size_t stack_size);
|
void AddTableEntry(uint8_t* code, size_t code_size, size_t stack_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
X64CodeCache::X64CodeCache(size_t chunk_size)
|
||||||
} // namespace x64
|
: chunk_size_(chunk_size), head_chunk_(NULL), active_chunk_(NULL) {}
|
||||||
} // namespace backend
|
|
||||||
} // namespace alloy
|
|
||||||
|
|
||||||
|
|
||||||
X64CodeCache::X64CodeCache(size_t chunk_size) :
|
|
||||||
chunk_size_(chunk_size),
|
|
||||||
head_chunk_(NULL), active_chunk_(NULL) {
|
|
||||||
lock_ = AllocMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
X64CodeCache::~X64CodeCache() {
|
X64CodeCache::~X64CodeCache() {
|
||||||
LockMutex(lock_);
|
std::lock_guard<std::mutex> guard(lock_);
|
||||||
auto chunk = head_chunk_;
|
auto chunk = head_chunk_;
|
||||||
while (chunk) {
|
while (chunk) {
|
||||||
auto next = chunk->next;
|
auto next = chunk->next;
|
||||||
|
@ -65,32 +53,30 @@ X64CodeCache::~X64CodeCache() {
|
||||||
chunk = next;
|
chunk = next;
|
||||||
}
|
}
|
||||||
head_chunk_ = NULL;
|
head_chunk_ = NULL;
|
||||||
UnlockMutex(lock_);
|
|
||||||
FreeMutex(lock_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64CodeCache::Initialize() {
|
int X64CodeCache::Initialize() { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
|
void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
|
||||||
size_t stack_size) {
|
size_t stack_size) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
|
size_t alloc_size = code_size;
|
||||||
|
|
||||||
// Add unwind info into the allocation size. Keep things 16b aligned.
|
// 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
|
// Always move the code to land on 16b alignment. We do this by rounding up
|
||||||
// to 16b so that all offsets are aligned.
|
// 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_) {
|
||||||
if (active_chunk_->capacity - active_chunk_->offset < code_size) {
|
if (active_chunk_->capacity - active_chunk_->offset < alloc_size) {
|
||||||
auto next = active_chunk_->next;
|
auto next = active_chunk_->next;
|
||||||
if (!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_);
|
next = new X64CodeChunk(chunk_size_);
|
||||||
active_chunk_->next = next;
|
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;
|
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.
|
// 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.
|
// 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.
|
// 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;
|
return final_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
X64CodeChunk::X64CodeChunk(size_t chunk_size) :
|
X64CodeChunk::X64CodeChunk(size_t chunk_size)
|
||||||
next(NULL),
|
: next(NULL), capacity(chunk_size), buffer(0), offset(0) {
|
||||||
capacity(chunk_size), buffer(0), offset(0) {
|
buffer = (uint8_t*)VirtualAlloc(NULL, capacity, MEM_RESERVE | MEM_COMMIT,
|
||||||
buffer = (uint8_t*)VirtualAlloc(
|
|
||||||
NULL, capacity,
|
|
||||||
MEM_RESERVE | MEM_COMMIT,
|
|
||||||
PAGE_EXECUTE_READWRITE);
|
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);
|
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_count = 0;
|
||||||
fn_table_handle = 0;
|
fn_table_handle = 0;
|
||||||
RtlAddGrowableFunctionTable(
|
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
|
||||||
&fn_table_handle,
|
fn_table_capacity, (ULONG_PTR)buffer,
|
||||||
fn_table,
|
(ULONG_PTR)buffer + capacity);
|
||||||
fn_table_count,
|
|
||||||
fn_table_capacity,
|
|
||||||
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X64CodeChunk::~X64CodeChunk() {
|
X64CodeChunk::~X64CodeChunk() {
|
||||||
|
@ -214,33 +195,27 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
||||||
if (fn_table_count + 1 > fn_table_capacity) {
|
if (fn_table_count + 1 > fn_table_capacity) {
|
||||||
// Table exhausted, need to realloc. If this happens a lot we should tune
|
// Table exhausted, need to realloc. If this happens a lot we should tune
|
||||||
// the table size to prevent this.
|
// 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);
|
RtlDeleteGrowableFunctionTable(fn_table_handle);
|
||||||
size_t old_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
|
size_t old_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
|
||||||
size_t new_size = old_size * 2;
|
size_t new_size = old_size * 2;
|
||||||
auto new_table = (RUNTIME_FUNCTION*)xe_realloc(fn_table, old_size, new_size);
|
auto new_table = (RUNTIME_FUNCTION*)realloc(fn_table, new_size);
|
||||||
XEASSERTNOTNULL(new_table);
|
assert_not_null(new_table);
|
||||||
if (!new_table) {
|
if (!new_table) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fn_table = new_table;
|
fn_table = new_table;
|
||||||
fn_table_capacity *= 2;
|
fn_table_capacity *= 2;
|
||||||
RtlAddGrowableFunctionTable(
|
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
|
||||||
&fn_table_handle,
|
fn_table_capacity, (ULONG_PTR)buffer,
|
||||||
fn_table,
|
(ULONG_PTR)buffer + capacity);
|
||||||
fn_table_count,
|
|
||||||
fn_table_capacity,
|
|
||||||
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate unwind data. We know we have space because we overallocated.
|
// Allocate unwind data. We know we have space because we overallocated.
|
||||||
// This should be the tailing 16b with 16b alignment.
|
// This should be the tailing 16b with 16b alignment.
|
||||||
size_t unwind_info_offset = offset;
|
size_t unwind_info_offset = offset - UNWIND_INFO_SIZE;
|
||||||
offset += UNWIND_INFO_SIZE;
|
|
||||||
|
|
||||||
if (!stack_size) {
|
if (!stack_size) {
|
||||||
uint8_t prolog_size = 0;
|
|
||||||
|
|
||||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||||
UNWIND_INFO* unwind_info = (UNWIND_INFO*)(buffer + unwind_info_offset);
|
UNWIND_INFO* unwind_info = (UNWIND_INFO*)(buffer + unwind_info_offset);
|
||||||
unwind_info->Version = 1;
|
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
|
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||||
size_t co = 0;
|
size_t co = 0;
|
||||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
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.UnwindOp = UWOP_ALLOC_SMALL;
|
||||||
unwind_code.OpInfo = stack_size / 8 - 1;
|
unwind_code.OpInfo = stack_size / 8 - 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -283,7 +259,8 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
|
||||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||||
size_t co = 0;
|
size_t co = 0;
|
||||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
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.UnwindOp = UWOP_ALLOC_LARGE;
|
||||||
unwind_code.OpInfo = 0;
|
unwind_code.OpInfo = 0;
|
||||||
unwind_code = unwind_info->UnwindCode[co++];
|
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.
|
// Notify the function table that it has new entries.
|
||||||
RtlGrowFunctionTable(fn_table_handle, fn_table_count);
|
RtlGrowFunctionTable(fn_table_handle, fn_table_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace x64
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
|
@ -7,75 +7,70 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <alloy/backend/x64/x64_emitter.h>
|
#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/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 alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace x64 {
|
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 MAX_CODE_SIZE = 1 * 1024 * 1024;
|
||||||
|
|
||||||
static const size_t STASH_OFFSET = 32;
|
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,
|
// 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
|
// otherwise our calls out to C to print will clear it before DID_CARRY/etc
|
||||||
// can get the value.
|
// can get the value.
|
||||||
#define STORE_EFLAGS 1
|
#define STORE_EFLAGS 1
|
||||||
|
|
||||||
} // namespace x64
|
|
||||||
} // namespace backend
|
|
||||||
} // namespace alloy
|
|
||||||
|
|
||||||
|
|
||||||
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
|
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
|
||||||
Operand::RBX,
|
Operand::RBX, Operand::R12, Operand::R13, Operand::R14, Operand::R15,
|
||||||
Operand::R12, Operand::R13, Operand::R14, Operand::R15,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
|
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)
|
||||||
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) :
|
: CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator),
|
||||||
runtime_(backend->runtime()),
|
runtime_(backend->runtime()),
|
||||||
backend_(backend),
|
backend_(backend),
|
||||||
code_cache_(backend->code_cache()),
|
code_cache_(backend->code_cache()),
|
||||||
allocator_(allocator),
|
allocator_(allocator),
|
||||||
current_instr_(0),
|
current_instr_(0) {}
|
||||||
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
|
|
||||||
}
|
|
||||||
|
|
||||||
X64Emitter::~X64Emitter() {
|
X64Emitter::~X64Emitter() {}
|
||||||
}
|
|
||||||
|
|
||||||
int X64Emitter::Initialize() {
|
int X64Emitter::Initialize() { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int X64Emitter::Emit(
|
int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
HIRBuilder* builder,
|
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
|
||||||
void*& out_code_address, size_t& out_code_size) {
|
void*& out_code_address, size_t& out_code_size) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
|
@ -84,6 +79,7 @@ int X64Emitter::Emit(
|
||||||
source_map_count_ = 0;
|
source_map_count_ = 0;
|
||||||
source_map_arena_.Reset();
|
source_map_arena_.Reset();
|
||||||
}
|
}
|
||||||
|
trace_flags_ = trace_flags;
|
||||||
|
|
||||||
// Fill the generator with code.
|
// Fill the generator with code.
|
||||||
size_t stack_size = 0;
|
size_t stack_size = 0;
|
||||||
|
@ -99,8 +95,7 @@ int X64Emitter::Emit(
|
||||||
// Stash source map.
|
// Stash source map.
|
||||||
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
|
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
|
||||||
debug_info->InitializeSourceMap(
|
debug_info->InitializeSourceMap(
|
||||||
source_map_count_,
|
source_map_count_, (SourceMapEntry*)source_map_arena_.CloneContents());
|
||||||
(SourceMapEntry*)source_map_arena_.CloneContents());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -129,13 +124,13 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
auto slot = *it;
|
auto slot = *it;
|
||||||
size_t type_size = GetTypeSize(slot->type);
|
size_t type_size = GetTypeSize(slot->type);
|
||||||
// Align to natural size.
|
// 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);
|
slot->set_constant((uint32_t)stack_offset);
|
||||||
stack_offset += type_size;
|
stack_offset += type_size;
|
||||||
}
|
}
|
||||||
// Ensure 16b alignment.
|
// Ensure 16b alignment.
|
||||||
stack_offset -= StackLayout::GUEST_STACK_SIZE;
|
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.
|
// Function prolog.
|
||||||
// Must be 16b aligned.
|
// 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!
|
// Adding or changing anything here must be matched!
|
||||||
const bool emit_prolog = true;
|
const bool emit_prolog = true;
|
||||||
const size_t stack_size = StackLayout::GUEST_STACK_SIZE + stack_offset;
|
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;
|
out_stack_size = stack_size;
|
||||||
stack_size_ = stack_size;
|
stack_size_ = stack_size;
|
||||||
if (emit_prolog) {
|
if (emit_prolog) {
|
||||||
|
@ -161,6 +156,19 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
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.
|
// Body.
|
||||||
auto block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
|
@ -175,10 +183,20 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
const Instr* instr = block->instr_head;
|
const Instr* instr = block->instr_head;
|
||||||
while (instr) {
|
while (instr) {
|
||||||
const Instr* new_tail = 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)) {
|
if (!SelectSequence(*this, instr, &new_tail)) {
|
||||||
// No sequence found!
|
// No sequence found!
|
||||||
XEASSERTALWAYS();
|
assert_always();
|
||||||
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
|
PLOGE("Unable to process HIR opcode %s", instr->opcode->name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
instr = new_tail;
|
instr = new_tail;
|
||||||
|
@ -189,6 +207,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
|
|
||||||
// Function epilog.
|
// Function epilog.
|
||||||
L("epilog");
|
L("epilog");
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
if (emit_prolog) {
|
if (emit_prolog) {
|
||||||
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
|
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
|
||||||
add(rsp, (uint32_t)stack_size);
|
add(rsp, (uint32_t)stack_size);
|
||||||
|
@ -214,21 +233,160 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
|
||||||
source_map_count_++;
|
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() {
|
void X64Emitter::DebugBreak() {
|
||||||
// TODO(benvanik): notify debugger.
|
// TODO(benvanik): notify debugger.
|
||||||
db(0xCC);
|
db(0xCC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::Trap() {
|
void X64Emitter::Trap(uint16_t trap_type) {
|
||||||
|
switch (trap_type) {
|
||||||
|
case 20:
|
||||||
// 0x0FE00014 is a 'debug print' where r3 = buffer r4 = length
|
// 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.
|
// TODO(benvanik): post software interrupt to debugger.
|
||||||
|
if (FLAGS_break_on_debugbreak) {
|
||||||
db(0xCC);
|
db(0xCC);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PLOGW("Unknown trap type %d", trap_type);
|
||||||
|
db(0xCC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
||||||
// TODO(benvanik): notify debugger.
|
// TODO(benvanik): notify debugger.
|
||||||
db(0xCC);
|
db(0xCC);
|
||||||
XEASSERTALWAYS();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total size of ResolveFunctionSymbol call site in bytes.
|
// 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 TOTAL_RESOLVE_SIZE = 27;
|
||||||
const size_t ASM_OFFSET = 2 + 2 + 8 + 2 + 8;
|
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) {
|
uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||||
|
@ -255,13 +402,18 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
// Resolve function. This will demand compile as required.
|
// Resolve function. This will demand compile as required.
|
||||||
Function* fn = NULL;
|
Function* fn = NULL;
|
||||||
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
|
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
|
||||||
XEASSERTNOTNULL(fn);
|
assert_not_null(fn);
|
||||||
auto x64_fn = static_cast<X64Function*>(fn);
|
auto x64_fn = static_cast<X64Function*>(fn);
|
||||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||||
|
|
||||||
// Overwrite the call site.
|
// Overwrite the call site.
|
||||||
// The return address points to ReloadRCX work after the call.
|
// The return address points to ReloadRCX work after the call.
|
||||||
|
#if XE_LIKE_WIN32
|
||||||
uint64_t return_address = reinterpret_cast<uint64_t>(_ReturnAddress());
|
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)
|
#pragma pack(push, 1)
|
||||||
struct Asm {
|
struct Asm {
|
||||||
uint16_t mov_rax;
|
uint16_t mov_rax;
|
||||||
|
@ -272,6 +424,7 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
uint8_t mov_rcx[5];
|
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);
|
Asm* code = reinterpret_cast<Asm*>(return_address - ASM_OFFSET);
|
||||||
code->rax_constant = addr;
|
code->rax_constant = addr;
|
||||||
code->call_rax = 0x9066;
|
code->call_rax = 0x9066;
|
||||||
|
@ -280,10 +433,10 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
return addr;
|
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());
|
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
|
||||||
// Resolve address to the function to call and store in rax.
|
// Resolve address to the function to call and store in rax.
|
||||||
// TODO(benvanik): caching/etc. For now this makes debugging easier.
|
|
||||||
if (fn) {
|
if (fn) {
|
||||||
mov(rax, reinterpret_cast<uint64_t>(fn->machine_code()));
|
mov(rax, reinterpret_cast<uint64_t>(fn->machine_code()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,12 +450,15 @@ void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_inf
|
||||||
// 5b
|
// 5b
|
||||||
ReloadECX();
|
ReloadECX();
|
||||||
size_t total_size = getSize() - start;
|
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.
|
// EDX overwritten, don't bother reloading.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually jump/call to rax.
|
// Actually jump/call to rax.
|
||||||
if (instr->flags & CALL_TAIL) {
|
if (instr->flags & CALL_TAIL) {
|
||||||
|
// Since we skip the prolog we need to mark the return here.
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
|
|
||||||
// Pass the callers return address over.
|
// Pass the callers return address over.
|
||||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
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) {
|
uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
|
||||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||||
|
|
||||||
// TODO(benvanik): required?
|
// TODO(benvanik): required?
|
||||||
target_address &= 0xFFFFFFFF;
|
target_address &= 0xFFFFFFFF;
|
||||||
|
assert_not_zero(target_address);
|
||||||
|
|
||||||
Function* fn = NULL;
|
Function* fn = NULL;
|
||||||
thread_state->runtime()->ResolveFunction(target_address, &fn);
|
thread_state->runtime()->ResolveFunction(target_address, &fn);
|
||||||
XEASSERTNOTNULL(fn);
|
assert_not_null(fn);
|
||||||
auto x64_fn = static_cast<X64Function*>(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) {
|
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);
|
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()) {
|
if (reg.getIdx() != rdx.getIdx()) {
|
||||||
mov(rdx, reg);
|
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);
|
CallNative(ResolveFunctionAddress);
|
||||||
|
|
||||||
// Actually jump/call to rax.
|
// Actually jump/call to rax.
|
||||||
|
L(skip_resolve);
|
||||||
if (instr->flags & CALL_TAIL) {
|
if (instr->flags & CALL_TAIL) {
|
||||||
|
// Since we skip the prolog we need to mark the return here.
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
|
|
||||||
// Pass the callers return address over.
|
// Pass the callers return address over.
|
||||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
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]);
|
mov(rdx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
|
||||||
call(rax);
|
call(rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outLocalLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
|
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
|
||||||
XELOGW("undefined extern call to %.8X %s",
|
PLOGW("undefined extern call to %.8llX %s", symbol_info->address(),
|
||||||
symbol_info->address(),
|
symbol_info->name().c_str());
|
||||||
symbol_info->name());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info) {
|
void X64Emitter::CallExtern(const hir::Instr* instr,
|
||||||
XEASSERT(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
|
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()) {
|
if (!symbol_info->extern_handler()) {
|
||||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
||||||
} else {
|
} else {
|
||||||
|
@ -383,6 +645,19 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_
|
||||||
ReloadEDX();
|
ReloadEDX();
|
||||||
// rax = host return
|
// 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) {
|
void X64Emitter::CallNative(void* fn) {
|
||||||
|
@ -406,7 +681,8 @@ void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0)) {
|
||||||
ReloadEDX();
|
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(rdx, arg0);
|
||||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||||
call(rax);
|
call(rax);
|
||||||
|
@ -440,11 +716,27 @@ 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() {
|
void X64Emitter::LoadEflags() {
|
||||||
#if STORE_EFLAGS
|
#if STORE_EFLAGS
|
||||||
mov(eax, dword[rsp + STASH_OFFSET]);
|
mov(eax, dword[rsp + STASH_OFFSET]);
|
||||||
push(rax);
|
btr(eax, 0);
|
||||||
popf();
|
|
||||||
#else
|
#else
|
||||||
// EFLAGS already present.
|
// EFLAGS already present.
|
||||||
#endif // STORE_EFLAGS
|
#endif // STORE_EFLAGS
|
||||||
|
@ -453,18 +745,13 @@ void X64Emitter::LoadEflags() {
|
||||||
void X64Emitter::StoreEflags() {
|
void X64Emitter::StoreEflags() {
|
||||||
#if STORE_EFLAGS
|
#if STORE_EFLAGS
|
||||||
pushf();
|
pushf();
|
||||||
pop(qword[rsp + STASH_OFFSET]);
|
pop(dword[rsp + STASH_OFFSET]);
|
||||||
#else
|
#else
|
||||||
// EFLAGS should have CA set?
|
// EFLAGS should have CA set?
|
||||||
// (so long as we don't fuck with it)
|
// (so long as we don't fuck with it)
|
||||||
#endif // STORE_EFLAGS
|
#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) {
|
bool X64Emitter::ConstantFitsIn32Reg(uint64_t v) {
|
||||||
if ((v & ~0x7FFFFFFF) == 0) {
|
if ((v & ~0x7FFFFFFF) == 0) {
|
||||||
// Fits under 31 bits, so just load using normal mov.
|
// 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) {
|
Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
||||||
static const vec128_t xmm_consts[] = {
|
static const vec128_t xmm_consts[] = {
|
||||||
/* XMMZero */ vec128f(0.0f, 0.0f, 0.0f, 0.0f),
|
/* XMMZero */ vec128f(0.0f),
|
||||||
/* XMMOne */ vec128f(1.0f, 1.0f, 1.0f, 1.0f),
|
/* XMMOne */ vec128f(1.0f),
|
||||||
/* XMMNegativeOne */ 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),
|
/* XMMFFFF */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
|
||||||
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u, 0x00000000u, 0x00000000u),
|
0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||||
|
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u,
|
||||||
|
0x00000000u, 0x00000000u),
|
||||||
|
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u,
|
||||||
|
0x00000000u, 0x00000000u),
|
||||||
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
|
/* 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),
|
/* 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),
|
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||||
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
||||||
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
/* XMM3333 */ vec128f(3.0f, 3.0f, 3.0f, 3.0f),
|
||||||
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u),
|
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u,
|
||||||
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu),
|
0x80000000u, 0x80000000u),
|
||||||
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu, 0xFFFFFFFFu, 0x7FFFFFFFu),
|
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u,
|
||||||
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu),
|
0x00000000u, 0x80000000u),
|
||||||
/* XMMPermuteControl15 */ vec128b(15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15),
|
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu,
|
||||||
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x0C000408u),
|
0x7FFFFFFFu, 0x7FFFFFFFu),
|
||||||
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du, 0xFFFFFF0Cu, 0xFFFFFF0Fu),
|
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu,
|
||||||
/* XMMOneOver255 */ vec128f(1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f),
|
0xFFFFFFFFu, 0x7FFFFFFFu),
|
||||||
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu, 0x0000001Fu),
|
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u,
|
||||||
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu, 0x000000FFu, 0x000000FFu),
|
0x08090A0Bu, 0x0C0D0E0Fu),
|
||||||
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0x00000000u),
|
/* XMMByteOrderMask */ vec128i(0x01000302u, 0x05040706u,
|
||||||
/* XMM255 */ vec128f(255.0f, 255.0f, 255.0f, 255.0f),
|
0x09080B0Au, 0x0D0C0F0Eu),
|
||||||
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u, 0x80808080u, 0x80808080u),
|
/* XMMPermuteControl15 */ vec128b(15),
|
||||||
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u, 0x80008000u, 0x80008000u),
|
/* XMMPermuteByteMask */ vec128b(0x1F),
|
||||||
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
/* XMMPackD3DCOLORSat */ vec128i(0x404000FFu),
|
||||||
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
/* 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
|
// TODO(benvanik): cache base pointer somewhere? stack? It'd be nice to
|
||||||
// prevent this move.
|
// prevent this move.
|
||||||
|
@ -559,7 +893,7 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, float v) {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
// 0
|
// 0
|
||||||
vpxor(dest, dest);
|
vpxor(dest, dest);
|
||||||
} else if (x.i == ~0UL) {
|
} else if (x.i == ~0U) {
|
||||||
// 1111...
|
// 1111...
|
||||||
vpcmpeqb(dest, dest);
|
vpcmpeqb(dest, dest);
|
||||||
} else {
|
} else {
|
||||||
|
@ -589,15 +923,12 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, double v) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Address X64Emitter::StashXmm(const Xmm& r) {
|
Address X64Emitter::StashXmm(int index, const Xmm& r) {
|
||||||
auto addr = ptr[rsp + STASH_OFFSET];
|
auto addr = ptr[rsp + STASH_OFFSET + (index * 16)];
|
||||||
vmovups(addr, r);
|
vmovups(addr, r);
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address X64Emitter::StashXmm(const vec128_t& v) {
|
} // namespace x64
|
||||||
auto addr = ptr[rsp + STASH_OFFSET];
|
} // namespace backend
|
||||||
LoadConstantXmm(xmm0, v);
|
} // namespace alloy
|
||||||
vmovups(addr, xmm0);
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,18 +10,21 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_EMITTER_H_
|
#ifndef ALLOY_BACKEND_X64_X64_EMITTER_H_
|
||||||
#define 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>
|
namespace alloy {
|
||||||
|
namespace hir {
|
||||||
#include <third_party/xbyak/xbyak/xbyak.h>
|
class HIRBuilder;
|
||||||
|
class Instr;
|
||||||
XEDECLARECLASS2(alloy, hir, HIRBuilder);
|
} // namespace hir
|
||||||
XEDECLARECLASS2(alloy, hir, Instr);
|
namespace runtime {
|
||||||
XEDECLARECLASS2(alloy, runtime, DebugInfo);
|
class DebugInfo;
|
||||||
XEDECLARECLASS2(alloy, runtime, FunctionInfo);
|
class FunctionInfo;
|
||||||
XEDECLARECLASS2(alloy, runtime, Runtime);
|
class Runtime;
|
||||||
XEDECLARECLASS2(alloy, runtime, SymbolInfo);
|
class SymbolInfo;
|
||||||
|
} // namespace runtime
|
||||||
|
} // namespace alloy
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -39,29 +42,48 @@ enum XmmConst {
|
||||||
XMMZero = 0,
|
XMMZero = 0,
|
||||||
XMMOne,
|
XMMOne,
|
||||||
XMMNegativeOne,
|
XMMNegativeOne,
|
||||||
|
XMMFFFF,
|
||||||
XMMMaskX16Y16,
|
XMMMaskX16Y16,
|
||||||
XMMFlipX16Y16,
|
XMMFlipX16Y16,
|
||||||
XMMFixX16Y16,
|
XMMFixX16Y16,
|
||||||
XMMNormalizeX16Y16,
|
XMMNormalizeX16Y16,
|
||||||
XMM0001,
|
XMM0001,
|
||||||
XMM3301,
|
XMM3301,
|
||||||
|
XMM3333,
|
||||||
XMMSignMaskPS,
|
XMMSignMaskPS,
|
||||||
XMMSignMaskPD,
|
XMMSignMaskPD,
|
||||||
XMMAbsMaskPS,
|
XMMAbsMaskPS,
|
||||||
XMMAbsMaskPD,
|
XMMAbsMaskPD,
|
||||||
XMMByteSwapMask,
|
XMMByteSwapMask,
|
||||||
|
XMMByteOrderMask,
|
||||||
XMMPermuteControl15,
|
XMMPermuteControl15,
|
||||||
|
XMMPermuteByteMask,
|
||||||
|
XMMPackD3DCOLORSat,
|
||||||
XMMPackD3DCOLOR,
|
XMMPackD3DCOLOR,
|
||||||
XMMUnpackD3DCOLOR,
|
XMMUnpackD3DCOLOR,
|
||||||
|
XMMPackFLOAT16_2,
|
||||||
|
XMMUnpackFLOAT16_2,
|
||||||
|
XMMPackFLOAT16_4,
|
||||||
|
XMMUnpackFLOAT16_4,
|
||||||
|
XMMPackSHORT_2Min,
|
||||||
|
XMMPackSHORT_2Max,
|
||||||
|
XMMPackSHORT_2,
|
||||||
|
XMMUnpackSHORT_2,
|
||||||
XMMOneOver255,
|
XMMOneOver255,
|
||||||
|
XMMMaskEvenPI16,
|
||||||
|
XMMShiftMaskEvenPI16,
|
||||||
XMMShiftMaskPS,
|
XMMShiftMaskPS,
|
||||||
XMMShiftByteMask,
|
XMMShiftByteMask,
|
||||||
|
XMMSwapWordMask,
|
||||||
XMMUnsignedDwordMax,
|
XMMUnsignedDwordMax,
|
||||||
XMM255,
|
XMM255,
|
||||||
|
XMMPI32,
|
||||||
XMMSignMaskI8,
|
XMMSignMaskI8,
|
||||||
XMMSignMaskI16,
|
XMMSignMaskI16,
|
||||||
XMMSignMaskI32,
|
XMMSignMaskI32,
|
||||||
XMMSignMaskF32,
|
XMMSignMaskF32,
|
||||||
|
XMMShortMinPS,
|
||||||
|
XMMShortMaxPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
||||||
|
@ -80,8 +102,8 @@ public:
|
||||||
|
|
||||||
int Initialize();
|
int Initialize();
|
||||||
|
|
||||||
int Emit(hir::HIRBuilder* builder,
|
int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
|
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||||
void*& out_code_address, size_t& out_code_size);
|
void*& out_code_address, size_t& out_code_size);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -117,29 +139,31 @@ public:
|
||||||
void MarkSourceOffset(const hir::Instr* i);
|
void MarkSourceOffset(const hir::Instr* i);
|
||||||
|
|
||||||
void DebugBreak();
|
void DebugBreak();
|
||||||
void Trap();
|
void Trap(uint16_t trap_type = 0);
|
||||||
void UnimplementedInstr(const hir::Instr* i);
|
void UnimplementedInstr(const hir::Instr* i);
|
||||||
void UnimplementedExtern(const hir::Instr* i);
|
void UnimplementedExtern(const hir::Instr* i);
|
||||||
|
|
||||||
void Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info);
|
void Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info);
|
||||||
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
|
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(void* fn);
|
||||||
void CallNative(uint64_t (*fn)(void* raw_context));
|
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));
|
||||||
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0);
|
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
|
||||||
|
uint64_t arg0);
|
||||||
void CallNativeSafe(void* fn);
|
void CallNativeSafe(void* fn);
|
||||||
void SetReturnAddress(uint64_t value);
|
void SetReturnAddress(uint64_t value);
|
||||||
void ReloadECX();
|
void ReloadECX();
|
||||||
void ReloadEDX();
|
void ReloadEDX();
|
||||||
|
|
||||||
|
void nop(size_t length = 1);
|
||||||
|
|
||||||
// TODO(benvanik): Label for epilog (don't use strings).
|
// TODO(benvanik): Label for epilog (don't use strings).
|
||||||
|
|
||||||
void LoadEflags();
|
void LoadEflags();
|
||||||
void StoreEflags();
|
void StoreEflags();
|
||||||
|
|
||||||
uint32_t page_table_address() const;
|
|
||||||
|
|
||||||
// Moves a 64bit immediate into memory.
|
// Moves a 64bit immediate into memory.
|
||||||
bool ConstantFitsIn32Reg(uint64_t v);
|
bool ConstantFitsIn32Reg(uint64_t v);
|
||||||
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
|
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
|
||||||
|
@ -148,14 +172,17 @@ public:
|
||||||
void LoadConstantXmm(Xbyak::Xmm dest, float v);
|
void LoadConstantXmm(Xbyak::Xmm dest, float v);
|
||||||
void LoadConstantXmm(Xbyak::Xmm dest, double v);
|
void LoadConstantXmm(Xbyak::Xmm dest, double v);
|
||||||
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
|
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
|
||||||
Xbyak::Address StashXmm(const Xbyak::Xmm& r);
|
Xbyak::Address StashXmm(int index, const Xbyak::Xmm& r);
|
||||||
Xbyak::Address StashXmm(const vec128_t& v);
|
|
||||||
|
|
||||||
size_t stack_size() const { return stack_size_; }
|
size_t stack_size() const { return stack_size_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* Emplace(size_t stack_size);
|
void* Emplace(size_t stack_size);
|
||||||
int Emit(hir::HIRBuilder* builder, size_t& out_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_;
|
runtime::Runtime* runtime_;
|
||||||
|
@ -170,14 +197,14 @@ protected:
|
||||||
|
|
||||||
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 gpr_reg_map_[GPR_COUNT];
|
||||||
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_EMITTER_H_
|
#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/backend/x64/x64_backend.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/thread_state.h"
|
||||||
#include <alloy/runtime/thread_state.h>
|
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::backend;
|
namespace backend {
|
||||||
using namespace alloy::backend::x64;
|
namespace x64 {
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
using alloy::runtime::Breakpoint;
|
||||||
|
using alloy::runtime::Function;
|
||||||
|
using alloy::runtime::FunctionInfo;
|
||||||
|
using alloy::runtime::ThreadState;
|
||||||
|
|
||||||
X64Function::X64Function(FunctionInfo* symbol_info) :
|
X64Function::X64Function(FunctionInfo* symbol_info)
|
||||||
machine_code_(NULL), code_size_(0),
|
: Function(symbol_info), machine_code_(nullptr), code_size_(0) {}
|
||||||
Function(symbol_info) {
|
|
||||||
}
|
|
||||||
|
|
||||||
X64Function::~X64Function() {
|
X64Function::~X64Function() {
|
||||||
// machine_code_ is freed by code cache.
|
// 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;
|
code_size_ = code_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) {
|
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) {
|
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
||||||
auto backend = (X64Backend*)thread_state->runtime()->backend();
|
auto backend = (X64Backend*)thread_state->runtime()->backend();
|
||||||
auto thunk = backend->host_to_guest_thunk();
|
auto thunk = backend->host_to_guest_thunk();
|
||||||
thunk(
|
thunk(machine_code_, thread_state->raw_context(), (void*)return_address);
|
||||||
machine_code_,
|
|
||||||
thread_state->raw_context(),
|
|
||||||
(void*)return_address);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace x64
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,16 +10,13 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
#ifndef ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
||||||
#define 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/function.h>
|
#include "alloy/runtime/symbol_info.h"
|
||||||
#include <alloy/runtime/symbol_info.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace x64 {
|
namespace x64 {
|
||||||
|
|
||||||
|
|
||||||
class X64Function : public runtime::Function {
|
class X64Function : public runtime::Function {
|
||||||
public:
|
public:
|
||||||
X64Function(runtime::FunctionInfo* symbol_info);
|
X64Function(runtime::FunctionInfo* symbol_info);
|
||||||
|
@ -41,10 +38,8 @@ private:
|
||||||
size_t code_size_;
|
size_t code_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_FUNCTION_H_
|
#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;
|
bool is_constant;
|
||||||
virtual bool ConstantFitsIn32Reg() const { return true; }
|
virtual bool ConstantFitsIn32Reg() const { return true; }
|
||||||
const REG_TYPE& reg() const {
|
const REG_TYPE& reg() const {
|
||||||
XEASSERT(!is_constant);
|
assert_true(!is_constant);
|
||||||
return reg_;
|
return reg_;
|
||||||
}
|
}
|
||||||
operator const REG_TYPE&() const {
|
operator const REG_TYPE&() const {
|
||||||
|
@ -183,33 +183,37 @@ protected:
|
||||||
|
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct I8 : ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> {
|
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 {
|
const int8_t constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.i8;
|
return BASE::value->constant.i8;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct I16 : ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> {
|
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 {
|
const int16_t constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.i16;
|
return BASE::value->constant.i16;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct I32 : ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> {
|
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 {
|
const int32_t constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.i32;
|
return BASE::value->constant.i32;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
|
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 {
|
const int64_t constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.i64;
|
return BASE::value->constant.i64;
|
||||||
}
|
}
|
||||||
bool ConstantFitsIn32Reg() const override {
|
bool ConstantFitsIn32Reg() const override {
|
||||||
int64_t v = value->constant.i64;
|
int64_t v = BASE::value->constant.i64;
|
||||||
if ((v & ~0x7FFFFFFF) == 0) {
|
if ((v & ~0x7FFFFFFF) == 0) {
|
||||||
// Fits under 31 bits, so just load using normal mov.
|
// Fits under 31 bits, so just load using normal mov.
|
||||||
return true;
|
return true;
|
||||||
|
@ -222,23 +226,26 @@ struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct F32 : ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> {
|
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 {
|
const float constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.f32;
|
return BASE::value->constant.f32;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct F64 : ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> {
|
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 {
|
const double constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.f64;
|
return BASE::value->constant.f64;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int TAG = -1>
|
template <int TAG = -1>
|
||||||
struct V128 : ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> {
|
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 {
|
const vec128_t& constant() const {
|
||||||
XEASSERT(is_constant);
|
assert_true(BASE::is_constant);
|
||||||
return value->constant.v128;
|
return BASE::value->constant.v128;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -308,6 +315,7 @@ template <hir::Opcode OPCODE, typename... Ts>
|
||||||
struct I;
|
struct I;
|
||||||
template <hir::Opcode OPCODE, typename DEST>
|
template <hir::Opcode OPCODE, typename DEST>
|
||||||
struct I<OPCODE, DEST> : DestField<DEST> {
|
struct I<OPCODE, DEST> : DestField<DEST> {
|
||||||
|
typedef DestField<DEST> BASE;
|
||||||
static const hir::Opcode opcode = OPCODE;
|
static const hir::Opcode opcode = OPCODE;
|
||||||
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type>::value;
|
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type>::value;
|
||||||
static const KeyType dest_type = DEST::key_type;
|
static const KeyType dest_type = DEST::key_type;
|
||||||
|
@ -316,7 +324,7 @@ protected:
|
||||||
template <typename... Ti> friend struct SequenceFields;
|
template <typename... Ti> friend struct SequenceFields;
|
||||||
bool Load(const Instr* i, TagTable& tag_table) {
|
bool Load(const Instr* i, TagTable& tag_table) {
|
||||||
if (InstrKey(i).value == key &&
|
if (InstrKey(i).value == key &&
|
||||||
LoadDest(i, tag_table)) {
|
BASE::LoadDest(i, tag_table)) {
|
||||||
instr = i;
|
instr = i;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -325,6 +333,7 @@ protected:
|
||||||
};
|
};
|
||||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
|
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
|
||||||
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
|
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
|
||||||
|
typedef DestField<DEST> BASE;
|
||||||
static const hir::Opcode opcode = OPCODE;
|
static const hir::Opcode opcode = OPCODE;
|
||||||
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
|
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
|
||||||
static const KeyType dest_type = DEST::key_type;
|
static const KeyType dest_type = DEST::key_type;
|
||||||
|
@ -335,7 +344,7 @@ protected:
|
||||||
template <typename... Ti> friend struct SequenceFields;
|
template <typename... Ti> friend struct SequenceFields;
|
||||||
bool Load(const Instr* i, TagTable& tag_table) {
|
bool Load(const Instr* i, TagTable& tag_table) {
|
||||||
if (InstrKey(i).value == key &&
|
if (InstrKey(i).value == key &&
|
||||||
LoadDest(i, tag_table) &&
|
BASE::LoadDest(i, tag_table) &&
|
||||||
tag_table.CheckTag<SRC1>(i->src1)) {
|
tag_table.CheckTag<SRC1>(i->src1)) {
|
||||||
instr = i;
|
instr = i;
|
||||||
src1.Load(i->src1);
|
src1.Load(i->src1);
|
||||||
|
@ -346,6 +355,7 @@ protected:
|
||||||
};
|
};
|
||||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
|
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
|
||||||
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
|
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
|
||||||
|
typedef DestField<DEST> BASE;
|
||||||
static const hir::Opcode opcode = OPCODE;
|
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 uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type>::value;
|
||||||
static const KeyType dest_type = DEST::key_type;
|
static const KeyType dest_type = DEST::key_type;
|
||||||
|
@ -358,7 +368,7 @@ protected:
|
||||||
template <typename... Ti> friend struct SequenceFields;
|
template <typename... Ti> friend struct SequenceFields;
|
||||||
bool Load(const Instr* i, TagTable& tag_table) {
|
bool Load(const Instr* i, TagTable& tag_table) {
|
||||||
if (InstrKey(i).value == key &&
|
if (InstrKey(i).value == key &&
|
||||||
LoadDest(i, tag_table) &&
|
BASE::LoadDest(i, tag_table) &&
|
||||||
tag_table.CheckTag<SRC1>(i->src1) &&
|
tag_table.CheckTag<SRC1>(i->src1) &&
|
||||||
tag_table.CheckTag<SRC2>(i->src2)) {
|
tag_table.CheckTag<SRC2>(i->src2)) {
|
||||||
instr = i;
|
instr = i;
|
||||||
|
@ -371,6 +381,7 @@ protected:
|
||||||
};
|
};
|
||||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2, typename SRC3>
|
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2, typename SRC3>
|
||||||
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
||||||
|
typedef DestField<DEST> BASE;
|
||||||
static const hir::Opcode opcode = OPCODE;
|
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 uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type, SRC3::key_type>::value;
|
||||||
static const KeyType dest_type = DEST::key_type;
|
static const KeyType dest_type = DEST::key_type;
|
||||||
|
@ -385,7 +396,7 @@ protected:
|
||||||
template <typename... Ti> friend struct SequenceFields;
|
template <typename... Ti> friend struct SequenceFields;
|
||||||
bool Load(const Instr* i, TagTable& tag_table) {
|
bool Load(const Instr* i, TagTable& tag_table) {
|
||||||
if (InstrKey(i).value == key &&
|
if (InstrKey(i).value == key &&
|
||||||
LoadDest(i, tag_table) &&
|
BASE::LoadDest(i, tag_table) &&
|
||||||
tag_table.CheckTag<SRC1>(i->src1) &&
|
tag_table.CheckTag<SRC1>(i->src1) &&
|
||||||
tag_table.CheckTag<SRC2>(i->src2) &&
|
tag_table.CheckTag<SRC2>(i->src2) &&
|
||||||
tag_table.CheckTag<SRC3>(i->src3)) {
|
tag_table.CheckTag<SRC3>(i->src3)) {
|
||||||
|
@ -404,7 +415,6 @@ struct SequenceFields;
|
||||||
template <typename I1>
|
template <typename I1>
|
||||||
struct SequenceFields<I1> {
|
struct SequenceFields<I1> {
|
||||||
I1 i1;
|
I1 i1;
|
||||||
typedef typename I1 I1Type;
|
|
||||||
protected:
|
protected:
|
||||||
template <typename SEQ, typename... Ti> friend struct Sequence;
|
template <typename SEQ, typename... Ti> friend struct Sequence;
|
||||||
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
|
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>
|
template <typename SEQ, typename T>
|
||||||
struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
|
typedef Sequence<SingleSequence<SEQ, T>, T> BASE;
|
||||||
typedef T EmitArgType;
|
typedef T EmitArgType;
|
||||||
static const uint32_t head_key = T::key;
|
// TODO(benvanik): find a way to do this cross-compiler.
|
||||||
static void Emit(X64Emitter& e, const EmitArgs& _) {
|
#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);
|
SEQ::Emit(e, _.i1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,12 +558,12 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
X64Emitter& e, const EmitArgType& i,
|
X64Emitter& e, const EmitArgType& i,
|
||||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
if (i.dest == i.src2) {
|
if (i.dest == i.src2) {
|
||||||
if (i.src1.ConstantFitsIn32Reg()) {
|
if (i.src1.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
|
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src1.constant());
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
}
|
}
|
||||||
|
@ -560,7 +576,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
if (i.src2.ConstantFitsIn32Reg()) {
|
if (i.src2.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src2.constant());
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
}
|
}
|
||||||
|
@ -584,9 +600,9 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
X64Emitter& e, const EmitArgType& i,
|
X64Emitter& e, const EmitArgType& i,
|
||||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
if (i.dest == i.src2) {
|
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(temp, i.src2);
|
||||||
e.mov(i.dest, i.src1.constant());
|
e.mov(i.dest, i.src1.constant());
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
|
@ -599,7 +615,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
if (i.src2.ConstantFitsIn32Reg()) {
|
if (i.src2.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src2.constant());
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
}
|
}
|
||||||
|
@ -608,7 +624,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
if (i.src2.ConstantFitsIn32Reg()) {
|
if (i.src2.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src2.constant());
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +633,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
if (i.dest == i.src1) {
|
if (i.dest == i.src1) {
|
||||||
reg_reg_fn(e, i.dest, i.src2);
|
reg_reg_fn(e, i.dest, i.src2);
|
||||||
} else if (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(temp, i.src2);
|
||||||
e.mov(i.dest, i.src1);
|
e.mov(i.dest, i.src1);
|
||||||
reg_reg_fn(e, i.dest, temp);
|
reg_reg_fn(e, i.dest, temp);
|
||||||
|
@ -632,7 +648,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
static void EmitCommutativeBinaryXmmOp(
|
static void EmitCommutativeBinaryXmmOp(
|
||||||
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
||||||
fn(e, i.dest, e.xmm0, i.src2);
|
fn(e, i.dest, e.xmm0, i.src2);
|
||||||
} else if (i.src2.is_constant) {
|
} else if (i.src2.is_constant) {
|
||||||
|
@ -647,7 +663,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
static void EmitAssociativeBinaryXmmOp(
|
static void EmitAssociativeBinaryXmmOp(
|
||||||
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
X64Emitter& e, const EmitArgType& i, const FN& fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
||||||
fn(e, i.dest, e.xmm0, i.src2);
|
fn(e, i.dest, e.xmm0, i.src2);
|
||||||
} else if (i.src2.is_constant) {
|
} else if (i.src2.is_constant) {
|
||||||
|
@ -663,11 +679,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
X64Emitter& e, const EmitArgType& i,
|
X64Emitter& e, const EmitArgType& i,
|
||||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
if (i.src1.ConstantFitsIn32Reg()) {
|
if (i.src1.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
|
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src1.constant());
|
||||||
reg_reg_fn(e, i.src2, temp);
|
reg_reg_fn(e, i.src2, temp);
|
||||||
}
|
}
|
||||||
|
@ -675,7 +691,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
if (i.src2.ConstantFitsIn32Reg()) {
|
if (i.src2.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
|
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src2.constant());
|
||||||
reg_reg_fn(e, i.src1, temp);
|
reg_reg_fn(e, i.src1, temp);
|
||||||
}
|
}
|
||||||
|
@ -688,11 +704,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
|
||||||
X64Emitter& e, const EmitArgType& i,
|
X64Emitter& e, const EmitArgType& i,
|
||||||
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
|
||||||
if (i.src1.is_constant) {
|
if (i.src1.is_constant) {
|
||||||
XEASSERT(!i.src2.is_constant);
|
assert_true(!i.src2.is_constant);
|
||||||
if (i.src1.ConstantFitsIn32Reg()) {
|
if (i.src1.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()), true);
|
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()), true);
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src1.constant());
|
||||||
reg_reg_fn(e, i.dest, i.src2, temp, true);
|
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()) {
|
if (i.src2.ConstantFitsIn32Reg()) {
|
||||||
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()), false);
|
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()), false);
|
||||||
} else {
|
} 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());
|
e.mov(temp, i.src2.constant());
|
||||||
reg_reg_fn(e, i.dest, i.src1, temp, false);
|
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 TAG6 = 6;
|
||||||
static const tag_t TAG7 = 7;
|
static const tag_t TAG7 = 7;
|
||||||
|
|
||||||
typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*, const Instr**);
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Register() {
|
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>
|
template <typename T, typename Tn, typename... Ts>
|
||||||
void Register() {
|
void Register() {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,9 +10,11 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
#ifndef ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
||||||
#define ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
#define ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
namespace alloy {
|
||||||
|
namespace hir {
|
||||||
XEDECLARECLASS2(alloy, hir, Instr);
|
class Instr;
|
||||||
|
} // namespace hir
|
||||||
|
} // namespace alloy
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -20,14 +22,12 @@ namespace x64 {
|
||||||
|
|
||||||
class X64Emitter;
|
class X64Emitter;
|
||||||
|
|
||||||
|
|
||||||
void RegisterSequences();
|
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 x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_SEQUENCES_H_
|
#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"
|
||||||
|
|
||||||
|
namespace alloy {
|
||||||
using namespace alloy;
|
namespace backend {
|
||||||
using namespace alloy::backend;
|
namespace x64 {
|
||||||
using namespace alloy::backend::x64;
|
|
||||||
|
|
||||||
using namespace Xbyak;
|
using namespace Xbyak;
|
||||||
|
|
||||||
|
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||||
|
: X64Emitter(backend, allocator) {}
|
||||||
|
|
||||||
X64ThunkEmitter::X64ThunkEmitter(
|
X64ThunkEmitter::~X64ThunkEmitter() {}
|
||||||
X64Backend* backend, XbyakAllocator* allocator) :
|
|
||||||
X64Emitter(backend, allocator) {
|
|
||||||
}
|
|
||||||
|
|
||||||
X64ThunkEmitter::~X64ThunkEmitter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
||||||
// rcx = target
|
// rcx = target
|
||||||
|
@ -123,6 +118,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
||||||
mov(rax, rdx);
|
mov(rax, rdx);
|
||||||
mov(rdx, r8);
|
mov(rdx, r8);
|
||||||
mov(r8, r9);
|
mov(r8, r9);
|
||||||
|
mov(r9, r10);
|
||||||
call(rax);
|
call(rax);
|
||||||
|
|
||||||
mov(rbx, qword[rsp + 48]);
|
mov(rbx, qword[rsp + 48]);
|
||||||
|
@ -143,3 +139,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
||||||
void* fn = Emplace(stack_size);
|
void* fn = Emplace(stack_size);
|
||||||
return (HostToGuestThunk)fn;
|
return (HostToGuestThunk)fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace x64
|
||||||
|
} // namespace backend
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -7,19 +7,16 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_CPU_X64_X64_THUNK_EMITTER_H_
|
#ifndef ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||||
#define XENIA_CPU_X64_X64_THUNK_EMITTER_H_
|
#define ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
|
||||||
#include <alloy/backend/x64/x64_backend.h>
|
|
||||||
#include <alloy/backend/x64/x64_emitter.h>
|
|
||||||
|
|
||||||
|
#include "alloy/backend/x64/x64_backend.h"
|
||||||
|
#include "alloy/backend/x64/x64_emitter.h"
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace x64 {
|
namespace x64 {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stack Layout
|
* Stack Layout
|
||||||
* ----------------------------
|
* ----------------------------
|
||||||
|
@ -99,14 +96,14 @@ 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 ...
|
* ... locals ...
|
||||||
* +------------------+
|
* +------------------+
|
||||||
|
@ -119,13 +116,12 @@ class StackLayout {
|
||||||
public:
|
public:
|
||||||
const static size_t THUNK_STACK_SIZE = 120;
|
const static size_t THUNK_STACK_SIZE = 120;
|
||||||
|
|
||||||
const static size_t GUEST_STACK_SIZE = 88;
|
const static size_t GUEST_STACK_SIZE = 104;
|
||||||
const static size_t GUEST_RCX_HOME = 64;
|
const static size_t GUEST_RCX_HOME = 80;
|
||||||
const static size_t GUEST_RET_ADDR = 72;
|
const static size_t GUEST_RET_ADDR = 88;
|
||||||
const static size_t GUEST_CALL_RET_ADDR = 80;
|
const static size_t GUEST_CALL_RET_ADDR = 96;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class X64ThunkEmitter : public X64Emitter {
|
class X64ThunkEmitter : public X64Emitter {
|
||||||
public:
|
public:
|
||||||
X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator);
|
X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator);
|
||||||
|
@ -138,10 +134,8 @@ public:
|
||||||
GuestToHostThunk EmitGuestToHostThunk();
|
GuestToHostThunk EmitGuestToHostThunk();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
#endif // ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
|
||||||
#endif // XENIA_CPU_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/backend/x64/x64_emitter.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
#include <alloy/runtime/thread_state.h>
|
#include "alloy/runtime/thread_state.h"
|
||||||
|
|
||||||
using namespace alloy;
|
using namespace alloy;
|
||||||
using namespace alloy::backend::x64;
|
using namespace alloy::backend::x64;
|
||||||
|
@ -27,9 +27,12 @@ namespace x64 {
|
||||||
#define TARGET_THREAD 1
|
#define TARGET_THREAD 1
|
||||||
|
|
||||||
#define IFLUSH() fflush(stdout)
|
#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 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 GetTracingMode() {
|
||||||
uint32_t mode = 0;
|
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) {
|
void TraceContextLoadI8(void* raw_context, uint64_t offset, uint8_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextLoadI16(void* raw_context, uint64_t offset, uint16_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
union {
|
auto v = _mm_loadu_pd(value);
|
||||||
double d;
|
DPRINT("%le (%llX) = ctx f64 +%llu\n", poly::m128_f64<0>(v),
|
||||||
uint64_t x;
|
poly::m128_i64<0>(v), offset);
|
||||||
} f;
|
|
||||||
f.x = value.m128_i64[0];
|
|
||||||
DPRINT("%lle (%llX) = ctx f64 +%d\n", f.d, value.m128_i64[0], offset);
|
|
||||||
}
|
}
|
||||||
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value) {
|
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%d\n",
|
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%llu\n",
|
||||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
|
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||||
offset);
|
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) {
|
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextStoreI16(void* raw_context, uint64_t offset, uint16_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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) {
|
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
union {
|
auto v = _mm_loadu_pd(value);
|
||||||
double d;
|
DPRINT("ctx f64 +%llu = %le (%llX)\n", offset, poly::m128_f64<0>(v),
|
||||||
uint64_t x;
|
poly::m128_i64<0>(v));
|
||||||
} f;
|
|
||||||
f.x = value.m128_i64[0];
|
|
||||||
DPRINT("ctx f64 +%d = %lle (%llX)\n", offset, value.m128_i64[0], f.d);
|
|
||||||
}
|
}
|
||||||
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value) {
|
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value) {
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("ctx v128 +%d = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", offset,
|
DPRINT("ctx v128 +%llu = [%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],
|
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("%d (%X) = load.i8 %.8X\n", (int8_t)value, value, address);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("%d (%X) = load.i16 %.8X\n", (int16_t)value, value, address);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("%d (%X) = load.i32 %.8X\n", (int32_t)value, value, address);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("%lld (%llX) = load.i64 %.8X\n", (int64_t)value, value, address);
|
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);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
union {
|
DPRINT("%le (%llX) = load.f64 %.8X\n", poly::m128_f64<0>(value),
|
||||||
double d;
|
poly::m128_i64<0>(value), address);
|
||||||
uint64_t x;
|
|
||||||
} f;
|
|
||||||
f.x = value.m128_i64[0];
|
|
||||||
DPRINT("%lle (%llX) = load.f64 %.8X\n", f.d, value.m128_i64[0], 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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = load.v128 %.8X\n",
|
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],
|
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
|
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
|
||||||
address);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("store.i8 %.8X = %d (%X)\n", address, (int8_t)value, value);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("store.i16 %.8X = %d (%X)\n", address, (int16_t)value, value);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("store.i32 %.8X = %d (%X)\n", address, (int32_t)value, value);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("store.i64 %.8X = %lld (%llX)\n", address, (int64_t)value, value);
|
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);
|
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
union {
|
DPRINT("store.f64 %.8X = %le (%llX)\n", address, poly::m128_f64<0>(value),
|
||||||
double d;
|
poly::m128_i64<0>(value));
|
||||||
uint64_t x;
|
|
||||||
} f;
|
|
||||||
f.x = value.m128_i64[0];
|
|
||||||
DPRINT("store.f64 %.8X = %lle (%llX)\n", address, f.d, value.m128_i64[0]);
|
|
||||||
}
|
}
|
||||||
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);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", address,
|
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n",
|
||||||
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
|
address, poly::m128_f32<0>(value), poly::m128_f32<1>(value),
|
||||||
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
|
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 x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
|
@ -10,24 +10,8 @@
|
||||||
#ifndef ALLOY_BACKEND_X64_X64_TRACERS_H_
|
#ifndef ALLOY_BACKEND_X64_X64_TRACERS_H_
|
||||||
#define ALLOY_BACKEND_X64_X64_TRACERS_H_
|
#define ALLOY_BACKEND_X64_X64_TRACERS_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
|
||||||
|
|
||||||
#if XE_LIKE_WIN32
|
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
#else
|
#include <cstdint>
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
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 TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value);
|
||||||
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value);
|
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value);
|
||||||
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 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 TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value);
|
||||||
|
|
||||||
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t 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 TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value);
|
||||||
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value);
|
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value);
|
||||||
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 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 TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value);
|
||||||
|
|
||||||
void TraceMemoryLoadI8(void* raw_context, uint64_t address, uint8_t value);
|
void TraceMemoryLoadI8(void* raw_context, uint32_t address, uint8_t value);
|
||||||
void TraceMemoryLoadI16(void* raw_context, uint64_t address, uint16_t value);
|
void TraceMemoryLoadI16(void* raw_context, uint32_t address, uint16_t value);
|
||||||
void TraceMemoryLoadI32(void* raw_context, uint64_t address, uint32_t value);
|
void TraceMemoryLoadI32(void* raw_context, uint32_t address, uint32_t value);
|
||||||
void TraceMemoryLoadI64(void* raw_context, uint64_t address, uint64_t value);
|
void TraceMemoryLoadI64(void* raw_context, uint32_t address, uint64_t value);
|
||||||
void TraceMemoryLoadF32(void* raw_context, uint64_t address, __m128 value);
|
void TraceMemoryLoadF32(void* raw_context, uint32_t address, __m128 value);
|
||||||
void TraceMemoryLoadF64(void* raw_context, uint64_t address, __m128 value);
|
void TraceMemoryLoadF64(void* raw_context, uint32_t address, __m128 value);
|
||||||
void TraceMemoryLoadV128(void* raw_context, uint64_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 TraceMemoryStoreI8(void* raw_context, uint32_t address, uint8_t value);
|
||||||
void TraceMemoryStoreI16(void* raw_context, uint64_t address, uint16_t value);
|
void TraceMemoryStoreI16(void* raw_context, uint32_t address, uint16_t value);
|
||||||
void TraceMemoryStoreI32(void* raw_context, uint64_t address, uint32_t value);
|
void TraceMemoryStoreI32(void* raw_context, uint32_t address, uint32_t value);
|
||||||
void TraceMemoryStoreI64(void* raw_context, uint64_t address, uint64_t value);
|
void TraceMemoryStoreI64(void* raw_context, uint32_t address, uint64_t value);
|
||||||
void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value);
|
void TraceMemoryStoreF32(void* raw_context, uint32_t address, __m128 value);
|
||||||
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value);
|
void TraceMemoryStoreF64(void* raw_context, uint32_t address, __m128 value);
|
||||||
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value);
|
void TraceMemoryStoreV128(void* raw_context, uint32_t address, __m128 value);
|
||||||
|
|
||||||
} // namespace x64
|
} // namespace x64
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_BACKEND_X64_X64_TRACERS_H_
|
#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/compiler_pass.h"
|
||||||
#include <alloy/compiler/tracing.h>
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::compiler;
|
namespace compiler {
|
||||||
using namespace alloy::hir;
|
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
using alloy::runtime::Runtime;
|
||||||
|
|
||||||
Compiler::Compiler(Runtime* runtime) :
|
Compiler::Compiler(Runtime* runtime) : runtime_(runtime) {}
|
||||||
runtime_(runtime) {
|
|
||||||
scratch_arena_ = new Arena();
|
|
||||||
|
|
||||||
alloy::tracing::WriteEvent(EventType::Init({
|
Compiler::~Compiler() { Reset(); }
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Compiler::~Compiler() {
|
void Compiler::AddPass(std::unique_ptr<CompilerPass> pass) {
|
||||||
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) {
|
|
||||||
pass->Initialize(this);
|
pass->Initialize(this);
|
||||||
passes_.push_back(pass);
|
passes_.push_back(std::move(pass));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::Reset() {
|
void Compiler::Reset() {}
|
||||||
}
|
|
||||||
|
|
||||||
int Compiler::Compile(HIRBuilder* builder) {
|
int Compiler::Compile(HIRBuilder* builder) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
// TODO(benvanik): sophisticated stuff. Run passes in parallel, run until they
|
// TODO(benvanik): sophisticated stuff. Run passes in parallel, run until they
|
||||||
// stop changing things, etc.
|
// stop changing things, etc.
|
||||||
for (auto it = passes_.begin(); it != passes_.end(); ++it) {
|
for (size_t i = 0; i < passes_.size(); ++i) {
|
||||||
CompilerPass* pass = *it;
|
auto& pass = passes_[i];
|
||||||
scratch_arena_->Reset();
|
scratch_arena_.Reset();
|
||||||
if (pass->Run(builder)) {
|
if (pass->Run(builder)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -63,3 +44,6 @@ int Compiler::Compile(HIRBuilder* builder) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,27 +10,31 @@
|
||||||
#ifndef ALLOY_COMPILER_COMPILER_H_
|
#ifndef ALLOY_COMPILER_COMPILER_H_
|
||||||
#define ALLOY_COMPILER_COMPILER_H_
|
#define ALLOY_COMPILER_COMPILER_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include <memory>
|
||||||
#include <alloy/hir/hir_builder.h>
|
#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 alloy {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
class CompilerPass;
|
class CompilerPass;
|
||||||
|
|
||||||
|
|
||||||
class Compiler {
|
class Compiler {
|
||||||
public:
|
public:
|
||||||
Compiler(runtime::Runtime* runtime);
|
Compiler(runtime::Runtime* runtime);
|
||||||
~Compiler();
|
~Compiler();
|
||||||
|
|
||||||
runtime::Runtime* runtime() const { return runtime_; }
|
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();
|
void Reset();
|
||||||
|
|
||||||
|
@ -38,15 +42,12 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
runtime::Runtime* runtime_;
|
runtime::Runtime* runtime_;
|
||||||
Arena* scratch_arena_;
|
Arena scratch_arena_;
|
||||||
|
|
||||||
typedef std::vector<CompilerPass*> PassList;
|
std::vector<std::unique_ptr<CompilerPass>> passes_;
|
||||||
PassList passes_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_COMPILER_H_
|
#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;
|
namespace alloy {
|
||||||
using namespace alloy::compiler;
|
namespace compiler {
|
||||||
|
|
||||||
|
CompilerPass::CompilerPass() : runtime_(0), compiler_(0) {}
|
||||||
|
|
||||||
CompilerPass::CompilerPass() :
|
CompilerPass::~CompilerPass() = default;
|
||||||
runtime_(0), compiler_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerPass::~CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
int CompilerPass::Initialize(Compiler* compiler) {
|
int CompilerPass::Initialize(Compiler* compiler) {
|
||||||
runtime_ = compiler->runtime();
|
runtime_ = compiler->runtime();
|
||||||
|
@ -31,3 +27,6 @@ int CompilerPass::Initialize(Compiler* compiler) {
|
||||||
Arena* CompilerPass::scratch_arena() const {
|
Arena* CompilerPass::scratch_arena() const {
|
||||||
return compiler_->scratch_arena();
|
return compiler_->scratch_arena();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,19 +10,19 @@
|
||||||
#ifndef ALLOY_COMPILER_COMPILER_PASS_H_
|
#ifndef ALLOY_COMPILER_COMPILER_PASS_H_
|
||||||
#define ALLOY_COMPILER_COMPILER_PASS_H_
|
#define ALLOY_COMPILER_COMPILER_PASS_H_
|
||||||
|
|
||||||
#include <alloy/core.h>
|
#include "alloy/hir/hir_builder.h"
|
||||||
|
|
||||||
#include <alloy/hir/hir_builder.h>
|
|
||||||
|
|
||||||
namespace alloy { namespace runtime { class Runtime; } }
|
|
||||||
|
|
||||||
|
namespace alloy {
|
||||||
|
namespace runtime {
|
||||||
|
class Runtime;
|
||||||
|
} // namespace runtime
|
||||||
|
} // namespace alloy
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
class Compiler;
|
class Compiler;
|
||||||
|
|
||||||
|
|
||||||
class CompilerPass {
|
class CompilerPass {
|
||||||
public:
|
public:
|
||||||
CompilerPass();
|
CompilerPass();
|
||||||
|
@ -40,9 +40,7 @@ protected:
|
||||||
Compiler* compiler_;
|
Compiler* compiler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_COMPILER_PASS_H_
|
#endif // ALLOY_COMPILER_COMPILER_PASS_H_
|
||||||
|
|
|
@ -10,17 +10,18 @@
|
||||||
#ifndef ALLOY_COMPILER_COMPILER_PASSES_H_
|
#ifndef ALLOY_COMPILER_COMPILER_PASSES_H_
|
||||||
#define ALLOY_COMPILER_COMPILER_PASSES_H_
|
#define ALLOY_COMPILER_COMPILER_PASSES_H_
|
||||||
|
|
||||||
#include <alloy/compiler/passes/constant_propagation_pass.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/context_promotion_pass.h>
|
#include "alloy/compiler/passes/control_flow_analysis_pass.h"
|
||||||
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
|
#include "alloy/compiler/passes/control_flow_simplification_pass.h"
|
||||||
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
|
#include "alloy/compiler/passes/data_flow_analysis_pass.h"
|
||||||
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
|
#include "alloy/compiler/passes/dead_code_elimination_pass.h"
|
||||||
#include <alloy/compiler/passes/finalization_pass.h>
|
//#include "alloy/compiler/passes/dead_store_elimination_pass.h"
|
||||||
#include <alloy/compiler/passes/register_allocation_pass.h>
|
#include "alloy/compiler/passes/finalization_pass.h"
|
||||||
#include <alloy/compiler/passes/simplification_pass.h>
|
#include "alloy/compiler/passes/register_allocation_pass.h"
|
||||||
#include <alloy/compiler/passes/validation_pass.h>
|
#include "alloy/compiler/passes/simplification_pass.h"
|
||||||
#include <alloy/compiler/passes/value_reduction_pass.h>
|
#include "alloy/compiler/passes/validation_pass.h"
|
||||||
|
#include "alloy/compiler/passes/value_reduction_pass.h"
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - mark_use/mark_set
|
// - mark_use/mark_set
|
||||||
|
@ -49,7 +50,6 @@
|
||||||
// Block gets:
|
// Block gets:
|
||||||
// AddIncomingValue(Value* value, Block* src_block) ??
|
// AddIncomingValue(Value* value, Block* src_block) ??
|
||||||
|
|
||||||
|
|
||||||
// Potentially interesting passes:
|
// Potentially interesting passes:
|
||||||
//
|
//
|
||||||
// Run order:
|
// 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/function.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::compiler;
|
namespace compiler {
|
||||||
using namespace alloy::compiler::passes;
|
namespace passes {
|
||||||
|
|
||||||
|
// TODO(benvanik): remove when enums redefined.
|
||||||
using namespace alloy::hir;
|
using namespace alloy::hir;
|
||||||
|
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
using alloy::hir::TypeName;
|
||||||
|
using alloy::hir::Value;
|
||||||
|
|
||||||
ConstantPropagationPass::ConstantPropagationPass() :
|
ConstantPropagationPass::ConstantPropagationPass() : CompilerPass() {}
|
||||||
CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstantPropagationPass::~ConstantPropagationPass() {
|
ConstantPropagationPass::~ConstantPropagationPass() {}
|
||||||
}
|
|
||||||
|
|
||||||
int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
@ -55,11 +58,11 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
// v1 = 19
|
// v1 = 19
|
||||||
// v2 = 0
|
// v2 = 0
|
||||||
|
|
||||||
Block* block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
Instr* i = block->instr_head;
|
auto i = block->instr_head;
|
||||||
while (i) {
|
while (i) {
|
||||||
Value* v = i->dest;
|
auto v = i->dest;
|
||||||
switch (i->opcode->num) {
|
switch (i->opcode->num) {
|
||||||
case OPCODE_DEBUG_BREAK_TRUE:
|
case OPCODE_DEBUG_BREAK_TRUE:
|
||||||
if (i->src1.value->IsConstant()) {
|
if (i->src1.value->IsConstant()) {
|
||||||
|
@ -275,13 +278,13 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_DID_CARRY:
|
case OPCODE_DID_CARRY:
|
||||||
XEASSERT(!i->src1.value->IsConstant());
|
assert_true(!i->src1.value->IsConstant());
|
||||||
break;
|
break;
|
||||||
case OPCODE_DID_OVERFLOW:
|
case OPCODE_DID_OVERFLOW:
|
||||||
XEASSERT(!i->src1.value->IsConstant());
|
assert_true(!i->src1.value->IsConstant());
|
||||||
break;
|
break;
|
||||||
case OPCODE_DID_SATURATE:
|
case OPCODE_DID_SATURATE:
|
||||||
XEASSERT(!i->src1.value->IsConstant());
|
assert_true(!i->src1.value->IsConstant());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_ADD:
|
case OPCODE_ADD:
|
||||||
|
@ -298,6 +301,31 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// TODO(benvanik): ADD_CARRY (w/ ARITHMETIC_SET_CARRY)
|
// 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:
|
case OPCODE_SUB:
|
||||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||||
v->set_from(i->src1.value);
|
v->set_from(i->src1.value);
|
||||||
|
@ -428,6 +456,10 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
// Quite a few of these, from building vec128s.
|
// Quite a few of these, from building vec128s.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Ignored.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
i = i->next;
|
i = i->next;
|
||||||
}
|
}
|
||||||
|
@ -438,7 +470,7 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
|
void ConstantPropagationPass::PropagateCarry(Value* v, bool did_carry) {
|
||||||
auto next = v->use_head;
|
auto next = v->use_head;
|
||||||
while (next) {
|
while (next) {
|
||||||
auto use = 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_
|
#ifndef ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||||
#define 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 alloy {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
namespace passes {
|
namespace passes {
|
||||||
|
|
||||||
|
|
||||||
class ConstantPropagationPass : public CompilerPass {
|
class ConstantPropagationPass : public CompilerPass {
|
||||||
public:
|
public:
|
||||||
ConstantPropagationPass();
|
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);
|
void PropagateCarry(hir::Value* v, bool did_carry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace passes
|
} // namespace passes
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
#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 <gflags/gflags.h>
|
||||||
|
|
||||||
#include <alloy/compiler/compiler.h>
|
#include "alloy/compiler/compiler.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
|
#include "xenia/profiling.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;
|
|
||||||
|
|
||||||
|
|
||||||
DEFINE_bool(store_all_context_values, false,
|
DEFINE_bool(store_all_context_values, false,
|
||||||
"Don't strip dead context stores to aid in debugging.");
|
"Don't strip dead context stores to aid in debugging.");
|
||||||
|
|
||||||
|
namespace alloy {
|
||||||
|
namespace compiler {
|
||||||
|
namespace passes {
|
||||||
|
|
||||||
ContextPromotionPass::ContextPromotionPass() :
|
// TODO(benvanik): remove when enums redefined.
|
||||||
context_values_size_(0), context_values_(0),
|
using namespace alloy::hir;
|
||||||
CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextPromotionPass::~ContextPromotionPass() {
|
using alloy::frontend::ContextInfo;
|
||||||
if (context_values_) {
|
using alloy::hir::Block;
|
||||||
xe_free(context_values_);
|
using alloy::hir::HIRBuilder;
|
||||||
}
|
using alloy::hir::Instr;
|
||||||
}
|
using alloy::hir::Value;
|
||||||
|
|
||||||
|
ContextPromotionPass::ContextPromotionPass() : CompilerPass() {}
|
||||||
|
|
||||||
|
ContextPromotionPass::~ContextPromotionPass() {}
|
||||||
|
|
||||||
int ContextPromotionPass::Initialize(Compiler* compiler) {
|
int ContextPromotionPass::Initialize(Compiler* compiler) {
|
||||||
if (CompilerPass::Initialize(compiler)) {
|
if (CompilerPass::Initialize(compiler)) {
|
||||||
|
@ -44,8 +42,8 @@ int ContextPromotionPass::Initialize(Compiler* compiler) {
|
||||||
|
|
||||||
// This is a terrible implementation.
|
// This is a terrible implementation.
|
||||||
ContextInfo* context_info = runtime_->frontend()->context_info();
|
ContextInfo* context_info = runtime_->frontend()->context_info();
|
||||||
context_values_size_ = context_info->size() * sizeof(Value*);
|
context_values_.resize(context_info->size());
|
||||||
context_values_ = (Value**)xe_calloc(context_values_size_);
|
context_validity_.resize(static_cast<uint32_t>(context_info->size()));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +68,7 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
|
||||||
|
|
||||||
// Promote loads to values.
|
// Promote loads to values.
|
||||||
// Process each block independently, for now.
|
// Process each block independently, for now.
|
||||||
Block* block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
PromoteBlock(block);
|
PromoteBlock(block);
|
||||||
block = block->next;
|
block = block->next;
|
||||||
|
@ -89,50 +87,55 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContextPromotionPass::PromoteBlock(Block* block) {
|
void ContextPromotionPass::PromoteBlock(Block* block) {
|
||||||
// Clear the context values list.
|
auto& validity = context_validity_;
|
||||||
// TODO(benvanik): new data structure that isn't so stupid.
|
validity.reset();
|
||||||
// Bitvector of validity, perhaps?
|
|
||||||
xe_zero_struct(context_values_, context_values_size_);
|
|
||||||
|
|
||||||
Instr* i = block->instr_head;
|
Instr* i = block->instr_head;
|
||||||
while (i) {
|
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;
|
size_t offset = i->src1.offset;
|
||||||
Value* previous_value = context_values_[offset];
|
if (validity.test(static_cast<uint32_t>(offset))) {
|
||||||
if (previous_value) {
|
|
||||||
// Legit previous value, reuse.
|
// Legit previous value, reuse.
|
||||||
|
Value* previous_value = context_values_[offset];
|
||||||
i->opcode = &hir::OPCODE_ASSIGN_info;
|
i->opcode = &hir::OPCODE_ASSIGN_info;
|
||||||
i->set_src1(previous_value);
|
i->set_src1(previous_value);
|
||||||
} else {
|
} else {
|
||||||
// Store the loaded value into the table.
|
// Store the loaded value into the table.
|
||||||
context_values_[offset] = i->dest;
|
context_values_[offset] = i->dest;
|
||||||
|
validity.set(static_cast<uint32_t>(offset));
|
||||||
}
|
}
|
||||||
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
|
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
|
||||||
size_t offset = i->src1.offset;
|
size_t offset = i->src1.offset;
|
||||||
Value* value = i->src2.value;
|
Value* value = i->src2.value;
|
||||||
// Store value into the table for later.
|
// Store value into the table for later.
|
||||||
context_values_[offset] = value;
|
context_values_[offset] = value;
|
||||||
|
validity.set(static_cast<uint32_t>(offset));
|
||||||
}
|
}
|
||||||
|
i = next;
|
||||||
i = i->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
|
void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
|
||||||
// TODO(benvanik): use a bitvector.
|
auto& validity = context_validity_;
|
||||||
// To avoid clearing the structure, we use a token.
|
validity.reset();
|
||||||
Value* token = (Value*)block;
|
|
||||||
|
|
||||||
// Walk backwards and mark offsets that are written to.
|
// Walk backwards and mark offsets that are written to.
|
||||||
// If the offset was written to earlier, ignore the store.
|
// If the offset was written to earlier, ignore the store.
|
||||||
Instr* i = block->instr_tail;
|
Instr* i = block->instr_tail;
|
||||||
while (i) {
|
while (i) {
|
||||||
Instr* prev = i->prev;
|
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;
|
size_t offset = i->src1.offset;
|
||||||
if (context_values_[offset] != token) {
|
if (!validity.test(static_cast<uint32_t>(offset))) {
|
||||||
// Mark offset as written to.
|
// Offset not yet written, mark and continue.
|
||||||
context_values_[offset] = token;
|
validity.set(static_cast<uint32_t>(offset));
|
||||||
} else {
|
} else {
|
||||||
// Already written to. Remove this store.
|
// Already written to. Remove this store.
|
||||||
i->Remove();
|
i->Remove();
|
||||||
|
@ -141,3 +144,7 @@ void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
|
||||||
i = prev;
|
i = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace passes
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,36 +10,43 @@
|
||||||
#ifndef ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
#ifndef ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
||||||
#define 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 alloy {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
namespace passes {
|
namespace passes {
|
||||||
|
|
||||||
|
|
||||||
class ContextPromotionPass : public CompilerPass {
|
class ContextPromotionPass : public CompilerPass {
|
||||||
public:
|
public:
|
||||||
ContextPromotionPass();
|
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 PromoteBlock(hir::Block* block);
|
||||||
void RemoveDeadStoresBlock(hir::Block* block);
|
void RemoveDeadStoresBlock(hir::Block* block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t context_values_size_;
|
std::vector<hir::Value*> context_values_;
|
||||||
hir::Value** context_values_;
|
llvm::BitVector context_validity_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace passes
|
} // namespace passes
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
|
#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/backend/backend.h"
|
||||||
#include <alloy/compiler/compiler.h>
|
#include "alloy/compiler/compiler.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::backend;
|
namespace compiler {
|
||||||
using namespace alloy::compiler;
|
namespace passes {
|
||||||
using namespace alloy::compiler::passes;
|
|
||||||
using namespace alloy::frontend;
|
// TODO(benvanik): remove when enums redefined.
|
||||||
using namespace alloy::hir;
|
using namespace alloy::hir;
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
using alloy::hir::Edge;
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
|
||||||
ControlFlowAnalysisPass::ControlFlowAnalysisPass() :
|
ControlFlowAnalysisPass::ControlFlowAnalysisPass() : CompilerPass() {}
|
||||||
CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {
|
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {}
|
||||||
}
|
|
||||||
|
|
||||||
int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
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.
|
// Add edges.
|
||||||
auto block = builder->first_block();
|
block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
auto instr = block->instr_tail;
|
auto instr = block->instr_tail;
|
||||||
while (instr) {
|
while (instr) {
|
||||||
|
@ -67,3 +74,7 @@ int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace passes
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,28 +10,24 @@
|
||||||
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
||||||
#define 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 alloy {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
namespace passes {
|
namespace passes {
|
||||||
|
|
||||||
|
|
||||||
class ControlFlowAnalysisPass : public CompilerPass {
|
class ControlFlowAnalysisPass : public CompilerPass {
|
||||||
public:
|
public:
|
||||||
ControlFlowAnalysisPass();
|
ControlFlowAnalysisPass();
|
||||||
virtual ~ControlFlowAnalysisPass();
|
~ControlFlowAnalysisPass() override;
|
||||||
|
|
||||||
virtual int Run(hir::HIRBuilder* builder);
|
int Run(hir::HIRBuilder* builder) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace passes
|
} // namespace passes
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
|
#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/backend/backend.h"
|
||||||
#include <alloy/compiler/compiler.h>
|
#include "alloy/compiler/compiler.h"
|
||||||
#include <alloy/runtime/runtime.h>
|
#include "alloy/runtime/runtime.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
|
|
||||||
|
#if XE_COMPILER_MSVC
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4244)
|
#pragma warning(disable : 4244)
|
||||||
#pragma warning(disable : 4267)
|
#pragma warning(disable : 4267)
|
||||||
#include <llvm/ADT/BitVector.h>
|
#include <llvm/ADT/BitVector.h>
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
#else
|
||||||
|
#include <llvm/ADT/BitVector.h>
|
||||||
|
#endif // XE_COMPILER_MSVC
|
||||||
|
|
||||||
using namespace alloy;
|
namespace alloy {
|
||||||
using namespace alloy::backend;
|
namespace compiler {
|
||||||
using namespace alloy::compiler;
|
namespace passes {
|
||||||
using namespace alloy::compiler::passes;
|
|
||||||
using namespace alloy::frontend;
|
// TODO(benvanik): remove when enums redefined.
|
||||||
using namespace alloy::hir;
|
using namespace alloy::hir;
|
||||||
using namespace alloy::runtime;
|
|
||||||
|
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
using alloy::hir::OpcodeSignatureType;
|
||||||
|
using alloy::hir::Value;
|
||||||
|
|
||||||
DataFlowAnalysisPass::DataFlowAnalysisPass() :
|
DataFlowAnalysisPass::DataFlowAnalysisPass() : CompilerPass() {}
|
||||||
CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFlowAnalysisPass::~DataFlowAnalysisPass() {
|
DataFlowAnalysisPass::~DataFlowAnalysisPass() {}
|
||||||
}
|
|
||||||
|
|
||||||
int DataFlowAnalysisPass::Run(HIRBuilder* builder) {
|
int DataFlowAnalysisPass::Run(HIRBuilder* builder) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
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.
|
// Stash for value map. We may want to maintain this during building.
|
||||||
auto arena = builder->arena();
|
auto arena = builder->arena();
|
||||||
Value** value_map = (Value**)arena->Alloc(
|
Value** value_map =
|
||||||
sizeof(Value*) * max_value_estimate);
|
(Value**)arena->Alloc(sizeof(Value*) * max_value_estimate);
|
||||||
|
|
||||||
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
|
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
|
||||||
// because they are only used during the block iteration.
|
// because they are only used during the block iteration.
|
||||||
// Mapped by block ordinal.
|
// Mapped by block ordinal.
|
||||||
// TODO(benvanik): cache this list, grow as needed, etc.
|
// TODO(benvanik): cache this list, grow as needed, etc.
|
||||||
auto incoming_bitvectors = (llvm::BitVector**)arena->Alloc(
|
auto incoming_bitvectors =
|
||||||
sizeof(llvm::BitVector*) * block_count);
|
(llvm::BitVector**)arena->Alloc(sizeof(llvm::BitVector*) * block_count);
|
||||||
for (auto n = 0u; n < block_count; n++) {
|
for (auto n = 0u; n < block_count; n++) {
|
||||||
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
|
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
|
||||||
}
|
}
|
||||||
|
@ -94,7 +98,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
||||||
if (v->def && v->def->block != block) { \
|
if (v->def && v->def->block != block) { \
|
||||||
incoming_values.set(v->ordinal); \
|
incoming_values.set(v->ordinal); \
|
||||||
} \
|
} \
|
||||||
XEASSERT(v->ordinal < max_value_estimate); \
|
assert_true(v->ordinal < max_value_estimate); \
|
||||||
value_map[v->ordinal] = v;
|
value_map[v->ordinal] = v;
|
||||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||||
SET_INCOMING_VALUE(instr->src1.value);
|
SET_INCOMING_VALUE(instr->src1.value);
|
||||||
|
@ -125,7 +129,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
||||||
auto outgoing_ordinal = outgoing_values.find_first();
|
auto outgoing_ordinal = outgoing_values.find_first();
|
||||||
while (outgoing_ordinal != -1) {
|
while (outgoing_ordinal != -1) {
|
||||||
Value* src_value = value_map[outgoing_ordinal];
|
Value* src_value = value_map[outgoing_ordinal];
|
||||||
XEASSERTNOTNULL(src_value);
|
assert_not_null(src_value);
|
||||||
if (!src_value->local_slot) {
|
if (!src_value->local_slot) {
|
||||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
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) {
|
while (def_next && def_next->opcode->flags & OPCODE_FLAG_PAIRED_PREV) {
|
||||||
def_next = def_next->next;
|
def_next = def_next->next;
|
||||||
}
|
}
|
||||||
XEASSERTNOTNULL(def_next);
|
assert_not_null(def_next);
|
||||||
builder->last_instr()->MoveBefore(def_next);
|
builder->last_instr()->MoveBefore(def_next);
|
||||||
|
|
||||||
// We don't need it in the incoming list.
|
// 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) {
|
while (tail && tail->opcode->flags & OPCODE_FLAG_BRANCH) {
|
||||||
tail = tail->prev;
|
tail = tail->prev;
|
||||||
}
|
}
|
||||||
XEASSERTNOTZERO(tail);
|
assert_not_zero(tail);
|
||||||
builder->last_instr()->MoveBefore(tail->next);
|
builder->last_instr()->MoveBefore(tail->next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +165,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
||||||
auto incoming_ordinal = incoming_values.find_first();
|
auto incoming_ordinal = incoming_values.find_first();
|
||||||
while (incoming_ordinal != -1) {
|
while (incoming_ordinal != -1) {
|
||||||
Value* src_value = value_map[incoming_ordinal];
|
Value* src_value = value_map[incoming_ordinal];
|
||||||
XEASSERTNOTNULL(src_value);
|
assert_not_null(src_value);
|
||||||
if (!src_value->local_slot) {
|
if (!src_value->local_slot) {
|
||||||
src_value->local_slot = builder->AllocLocal(src_value->type);
|
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);
|
builder->last_instr()->MoveBefore(block->instr_head);
|
||||||
|
|
||||||
// Swap uses of original value with the local value.
|
// Swap uses of original value with the local value.
|
||||||
auto instr = block->instr_head;
|
instr = block->instr_head;
|
||||||
while (instr) {
|
while (instr) {
|
||||||
uint32_t signature = instr->opcode->signature;
|
uint32_t signature = instr->opcode->signature;
|
||||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||||
|
@ -201,3 +205,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
|
||||||
delete incoming_bitvectors[n];
|
delete incoming_bitvectors[n];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace passes
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#ifndef ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
#ifndef ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
|
||||||
#define 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 {
|
namespace alloy {
|
||||||
|
@ -21,9 +21,9 @@ namespace passes {
|
||||||
class DataFlowAnalysisPass : public CompilerPass {
|
class DataFlowAnalysisPass : public CompilerPass {
|
||||||
public:
|
public:
|
||||||
DataFlowAnalysisPass();
|
DataFlowAnalysisPass();
|
||||||
virtual ~DataFlowAnalysisPass();
|
~DataFlowAnalysisPass() override;
|
||||||
|
|
||||||
virtual int Run(hir::HIRBuilder* builder);
|
int Run(hir::HIRBuilder* builder) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t LinearizeBlocks(hir::HIRBuilder* builder);
|
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;
|
#include "xenia/profiling.h"
|
||||||
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 namespace alloy::hir;
|
||||||
|
|
||||||
|
using alloy::hir::HIRBuilder;
|
||||||
|
using alloy::hir::Instr;
|
||||||
|
using alloy::hir::Value;
|
||||||
|
|
||||||
DeadCodeEliminationPass::DeadCodeEliminationPass() :
|
DeadCodeEliminationPass::DeadCodeEliminationPass() : CompilerPass() {}
|
||||||
CompilerPass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
DeadCodeEliminationPass::~DeadCodeEliminationPass() {
|
DeadCodeEliminationPass::~DeadCodeEliminationPass() {}
|
||||||
}
|
|
||||||
|
|
||||||
int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
@ -63,7 +67,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||||
|
|
||||||
bool any_instr_removed = false;
|
bool any_instr_removed = false;
|
||||||
bool any_locals_removed = false;
|
bool any_locals_removed = false;
|
||||||
Block* block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
// Walk instructions in reverse.
|
// Walk instructions in reverse.
|
||||||
Instr* i = block->instr_tail;
|
Instr* i = block->instr_tail;
|
||||||
|
@ -71,8 +75,8 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||||
auto prev = i->prev;
|
auto prev = i->prev;
|
||||||
|
|
||||||
auto opcode = i->opcode;
|
auto opcode = i->opcode;
|
||||||
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) &&
|
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) && i->dest &&
|
||||||
i->dest && !i->dest->use_head) {
|
!i->dest->use_head) {
|
||||||
// Has no uses and is not volatile. This instruction can die!
|
// Has no uses and is not volatile. This instruction can die!
|
||||||
MakeNopRecursive(i);
|
MakeNopRecursive(i);
|
||||||
any_instr_removed = true;
|
any_instr_removed = true;
|
||||||
|
@ -110,7 +114,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
|
||||||
|
|
||||||
// Remove all nops.
|
// Remove all nops.
|
||||||
if (any_instr_removed) {
|
if (any_instr_removed) {
|
||||||
Block* block = builder->first_block();
|
block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
Instr* i = block->instr_head;
|
Instr* i = block->instr_head;
|
||||||
while (i) {
|
while (i) {
|
||||||
|
@ -151,9 +155,9 @@ void DeadCodeEliminationPass::MakeNopRecursive(Instr* i) {
|
||||||
#define MAKE_NOP_SRC(n) \
|
#define MAKE_NOP_SRC(n) \
|
||||||
if (i->src##n##_use) { \
|
if (i->src##n##_use) { \
|
||||||
Value::Use* use = i->src##n##_use; \
|
Value::Use* use = i->src##n##_use; \
|
||||||
Value* value = i->src##n##.value; \
|
Value* value = i->src##n.value; \
|
||||||
i->src##n##_use = NULL; \
|
i->src##n##_use = NULL; \
|
||||||
i->src##n##.value = NULL; \
|
i->src##n.value = NULL; \
|
||||||
value->RemoveUse(use); \
|
value->RemoveUse(use); \
|
||||||
if (!value->use_head) { \
|
if (!value->use_head) { \
|
||||||
/* Value is now unused, so recursively kill it. */ \
|
/* Value is now unused, so recursively kill it. */ \
|
||||||
|
@ -190,7 +194,6 @@ void DeadCodeEliminationPass::ReplaceAssignment(Instr* i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
||||||
auto slot = i->src1.value;
|
|
||||||
auto src = i->src2.value;
|
auto src = i->src2.value;
|
||||||
|
|
||||||
auto use = src->use_head;
|
auto use = src->use_head;
|
||||||
|
@ -209,3 +212,7 @@ bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace passes
|
||||||
|
} // namespace compiler
|
||||||
|
} // namespace alloy
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#ifndef ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
|
#ifndef ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
|
||||||
#define 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 {
|
namespace alloy {
|
||||||
|
@ -21,9 +21,9 @@ namespace passes {
|
||||||
class DeadCodeEliminationPass : public CompilerPass {
|
class DeadCodeEliminationPass : public CompilerPass {
|
||||||
public:
|
public:
|
||||||
DeadCodeEliminationPass();
|
DeadCodeEliminationPass();
|
||||||
virtual ~DeadCodeEliminationPass();
|
~DeadCodeEliminationPass() override;
|
||||||
|
|
||||||
virtual int Run(hir::HIRBuilder* builder);
|
int Run(hir::HIRBuilder* builder) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MakeNopRecursive(hir::Instr* i);
|
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