Merge remote-tracking branch 'upstream/master'

This commit is contained in:
x1nixmzeng 2015-02-02 22:37:27 +00:00
commit 19ca4796be
1067 changed files with 134602 additions and 51251 deletions

15
.gitignore vendored
View File

@ -42,6 +42,7 @@ tmtags
*.py[co]
.coverage
*.o
# ==============================================================================
# Logs and dumps
@ -49,6 +50,7 @@ tmtags
npm-debug.log
private/
*.trace
# ==============================================================================
# Build system output
@ -70,3 +72,16 @@ build-out/
build-gen/
build-bin/
build-test/
# ==============================================================================
# Local-only paths
# ==============================================================================
.vagrant
attic/
third_party/binutils/binutils-2.24.tar.gz
third_party/binutils/bin/
third_party/binutils/powerpc-none-elf/
third_party/binutils/share/
third_party/binutils/binutils*
third_party/vasm/

21
.gitmodules vendored
View File

@ -1,9 +1,3 @@
[submodule "third_party/ninja"]
path = third_party/ninja
url = https://github.com/martine/ninja.git
[submodule "third_party/gyp"]
path = third_party/gyp
url = https://github.com/benvanik/gyp.git
[submodule "third_party/gflags"]
path = third_party/gflags
url = https://github.com/benvanik/gflags.git
@ -13,12 +7,15 @@
[submodule "third_party/beaengine"]
path = third_party/beaengine
url = https://github.com/benvanik/beaengine.git
[submodule "third_party/wslay"]
path = third_party/wslay
url = https://github.com/benvanik/wslay.git
[submodule "third_party/jansson"]
path = third_party/jansson
url = https://github.com/akheron/jansson.git
[submodule "third_party/xbyak"]
path = third_party/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "third_party/gyp"]
path = third_party/gyp
url = https://chromium.googlesource.com/external/gyp
[submodule "third_party/ninja"]
path = third_party/ninja
url = https://chromium.googlesource.com/external/martine/ninja
[submodule "third_party/catch"]
path = third_party/catch
url = https://github.com/philsquared/Catch.git

23
.travis.yml Normal file
View File

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

View File

@ -20,7 +20,7 @@ that.
## Status
* Some code runs. [Insert any game name here] doesn't.
* Some code runs. [Insert any game name here] doesn't. Except Frogger 2. Yep.
* Asserts! Crashes! Hangs! Blank screens!
## Disclaimer
@ -35,7 +35,7 @@ legally purchased devices and games and information made public on the internet
Windows 8.1+:
# install python 2.7 and VS2013
# install python 2.7 and VS2013/2015
git clone https://github.com/benvanik/xenia.git
cd xenia
xb setup
@ -47,7 +47,8 @@ update gyp files/etc.
## Building
See [building](docs/building.md) for setup and information about the
`xenia-build` script.
`xenia-build` script. When writing code, check the [style guide](docs/style_guide.md)
and be sure to clang-format!
## Contributors Wanted!
@ -55,19 +56,11 @@ Have some spare time, know advanced C++, and want to write an emulator?
Contribute! There's a ton of work that needs to be done, a lot of which
is wide open greenfield fun.
That said, the project is currently undergoing a lot of major foundational
development and core pieces are changing rapidly and poorly documented.
It'll be difficult to casually hack things in unless you know what you're
doing.
Fixes and optimizations are always welcome (please!), but in addition to
that there are some major work areas still untouched:
* Help work through missing functionality/bugs in game [compat](https://github.com/benvanik/xenia/issues?labels=compat)
* Write an [OpenGL driver](https://github.com/benvanik/xenia/issues/59)
* Add input drivers for [OSX](https://github.com/benvanik/xenia/issues/61) and [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
* Start [hacking on audio](https://github.com/benvanik/xenia/issues/62)
* Build a [virtual LIVE service](https://github.com/benvanik/xenia/issues/64)
* Add input drivers for [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
See more projects [good for contributors](https://github.com/benvanik/xenia/issues?labels=good+for+contributors&page=1&state=open). It's a good idea to ask on IRC/the bugs before beginning work
on something.
@ -91,15 +84,13 @@ enough to run games at a decent speed the answer is no.
### What about Linux/OSX?
The project is designed to support non-Windows platforms but until it's running
games it's not worth the maintenance burden. If you're a really passionate
Linux/OSX-based developer and want to help out, run Bootcamp/VM and contribute
an OpenGL 4 driver - that'll be the most difficult part in porting to
non-Windows platforms.
games it's not worth the maintenance burden. OSX will likely remain unsupported
until Apple supports OpenGL 4.3 or higher.
### What kind of GPU do I need?
DirectX 11 support is required. To get full speed and compatibility Mantle may
be required in the future.
OpenGL 4.5 support and drivers are required. To get full speed and compatibility
GL-next may eventually be required.
### Have you heard of LLVM/asmjit/jitasm/luajit/etc?

46
Vagrantfile vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
TODO: APU

View File

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

View File

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

View File

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

View File

@ -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 = '&#9679;';
} else {
el.innerHTML = '&#9676;';
}
} 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();
// });
});

View File

@ -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()">&times;</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>

View File

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

View File

@ -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()">&times;</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>

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
TODO: GPU

View File

@ -1 +0,0 @@
TODO: kernel

View File

@ -1 +0,0 @@
TODO: memory

View File

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

View File

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

View File

@ -1 +0,0 @@
<div ui-view></div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,19 +9,33 @@ video drivers for your card.
### Windows
* Windows 8 or 8.1
* Visual Studio 2013
* Visual Studio 2013+
* [Python 2.7](http://www.python.org/download/releases/2.7.6/)
* If you are on Windows 8, you will also need the [Windows 8.1 SDK](http://msdn.microsoft.com/en-us/windows/desktop/bg162891)
Ensure Python is in your PATH (`C:\Python27\`).
I recommend using [Cmder](http://bliker.github.io/cmder/) for git and command
line usage.
#### Debugging
VS behaves oddly with the debug paths. Open the xenia-run project properties
VS behaves oddly with the debug paths. Open the xenia project properties
and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
### OSX
* Mac OSX 10.9+
* Xcode 5.1+
#### Debugging
Choose `Product > Scheme > Edit Scheme`. For xenia, alloy-sandbox, and the
other executables select the Run action on the left and set
`Options > Working Directory` to your root xenia/ git path.
## xenia-build
A simple build script is included to manage basic tasks such as building
@ -44,7 +58,7 @@ keypresses:
#### setup
Run this on initial checkout to pull down all dependencies and setup LLVM.
Run this on initial checkout to pull down all dependencies and submodules.
xb setup
@ -95,14 +109,8 @@ switching between the debug and release variants with `--debug`.
To make life easier you can use `--flagfile=myflags.txt` to specify all
arguments, including using `--target=my.xex` to pick an executable.
### xenia-info
Dumps information about a xex file.
./bin/xenia-info some.xex
### xenia-run
### xenia
Runs a xex.
./bin/xenia-run some.xex
./bin/xenia some.xex

45
docs/style_guide.md Normal file
View File

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

6
src/README.md Normal file
View File

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

29
src/alloy/README.md Normal file
View File

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

View File

@ -10,11 +10,8 @@
#ifndef ALLOY_ALLOY_PRIVATE_H_
#define ALLOY_ALLOY_PRIVATE_H_
#include <alloy/core.h>
#include <gflags/gflags.h>
DECLARE_bool(debug);
DECLARE_bool(always_disasm);
@ -22,11 +19,6 @@ DECLARE_bool(validate_hir);
DECLARE_uint64(break_on_instruction);
DECLARE_uint64(break_on_memory);
namespace alloy {
} // namespace alloy
DECLARE_bool(break_on_debugbreak);
#endif // ALLOY_ALLOY_PRIVATE_H_

View File

@ -7,12 +7,11 @@
******************************************************************************
*/
#include <alloy/alloy.h>
#include <alloy/alloy-private.h>
#include "alloy/alloy.h"
#include "alloy/alloy-private.h"
using namespace alloy;
#if 0 && DEBUG
#define DEFAULT_DEBUG_FLAG true
#else
@ -21,7 +20,8 @@ using namespace alloy;
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
"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.");
DEFINE_bool(validate_hir, false,
@ -32,3 +32,4 @@ DEFINE_uint64(break_on_instruction, 0,
"int3 before the given guest address is executed.");
DEFINE_uint64(break_on_memory, 0,
"int3 on read/write to the given memory address.");
DEFINE_bool(break_on_debugbreak, true, "int3 on JITed __debugbreak requests.");

View File

@ -10,22 +10,9 @@
#ifndef ALLOY_ALLOY_H_
#define ALLOY_ALLOY_H_
#include <alloy/core.h>
#include <alloy/runtime/function.h>
#include <alloy/runtime/module.h>
#include <alloy/runtime/runtime.h>
#include <alloy/runtime/thread_state.h>
#include <alloy/tracing/tracing.h>
// TODO(benvanik): based on platform/config/etc.
#include <alloy/backend/ivm/ivm_backend.h>
namespace alloy {
} // namespace alloy
#include "alloy/runtime/function.h"
#include "alloy/runtime/module.h"
#include "alloy/runtime/runtime.h"
#include "alloy/runtime/thread_state.h"
#endif // ALLOY_ALLOY_H_

View File

@ -7,15 +7,14 @@
******************************************************************************
*/
#include <alloy/arena.h>
#include "alloy/arena.h"
using namespace alloy;
#include "poly/poly.h"
namespace alloy {
Arena::Arena(size_t chunk_size) :
chunk_size_(chunk_size),
head_chunk_(NULL), active_chunk_(NULL) {
}
Arena::Arena(size_t chunk_size)
: chunk_size_(chunk_size), head_chunk_(nullptr), active_chunk_(nullptr) {}
Arena::~Arena() {
Reset();
@ -25,7 +24,7 @@ Arena::~Arena() {
delete chunk;
chunk = next;
}
head_chunk_ = NULL;
head_chunk_ = nullptr;
}
void Arena::Reset() {
@ -48,7 +47,7 @@ void* Arena::Alloc(size_t size) {
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
Chunk* next = active_chunk_->next;
if (!next) {
XEASSERT(size < chunk_size_); // need to support larger chunks
assert_true(size < chunk_size_, "need to support larger chunks");
next = new Chunk(chunk_size_);
active_chunk_->next = next;
}
@ -74,11 +73,11 @@ void* Arena::CloneContents() {
}
chunk = chunk->next;
}
void* result = xe_malloc(total_length);
void* result = malloc(total_length);
uint8_t* p = (uint8_t*)result;
chunk = head_chunk_;
while (chunk) {
xe_copy_struct(p, chunk->buffer, chunk->offset);
memcpy(p, chunk->buffer, chunk->offset);
p += chunk->offset;
if (chunk == active_chunk_) {
break;
@ -88,14 +87,15 @@ void* Arena::CloneContents() {
return result;
}
Arena::Chunk::Chunk(size_t chunk_size) :
next(NULL),
capacity(chunk_size), buffer(0), offset(0) {
buffer = (uint8_t*)xe_malloc(capacity);
Arena::Chunk::Chunk(size_t chunk_size)
: next(nullptr), capacity(chunk_size), buffer(0), offset(0) {
buffer = reinterpret_cast<uint8_t*>(malloc(capacity));
}
Arena::Chunk::~Chunk() {
if (buffer) {
xe_free(buffer);
free(buffer);
}
}
} // namespace alloy

View File

@ -10,12 +10,11 @@
#ifndef ALLOY_ARENA_H_
#define ALLOY_ARENA_H_
#include <alloy/core.h>
#include <cstddef>
#include <cstdint>
namespace alloy {
class Arena {
public:
Arena(size_t chunk_size = 4 * 1024 * 1024);
@ -25,8 +24,9 @@ public:
void DebugFill();
void* Alloc(size_t size);
template<typename T> T* Alloc() {
return (T*)Alloc(sizeof(T));
template <typename T>
T* Alloc() {
return reinterpret_cast<T*>(Alloc(sizeof(T)));
}
void* CloneContents();
@ -50,8 +50,6 @@ private:
Chunk* active_chunk_;
};
} // namespace alloy
#endif // ALLOY_ARENA_H_

View File

@ -7,26 +7,18 @@
******************************************************************************
*/
#include <alloy/backend/assembler.h>
#include "alloy/backend/assembler.h"
#include <alloy/backend/tracing.h>
namespace alloy {
namespace backend {
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::runtime;
Assembler::Assembler(Backend* backend) : backend_(backend) {}
Assembler::~Assembler() { Reset(); }
Assembler::Assembler(Backend* backend) :
backend_(backend) {
}
int Assembler::Initialize() { return 0; }
Assembler::~Assembler() {
Reset();
}
void Assembler::Reset() {}
int Assembler::Initialize() {
return 0;
}
void Assembler::Reset() {
}
} // namespace backend
} // namespace alloy

View File

@ -10,27 +10,25 @@
#ifndef ALLOY_BACKEND_ASSEMBLER_H_
#define ALLOY_BACKEND_ASSEMBLER_H_
#include <alloy/core.h>
#include <memory>
namespace alloy {
namespace hir {
class HIRBuilder;
}
} // namespace hir
namespace runtime {
class DebugInfo;
class Function;
class FunctionInfo;
class Runtime;
}
}
} // namespace runtime
} // namespace alloy
namespace alloy {
namespace backend {
class Backend;
class Assembler {
public:
Assembler(Backend* backend);
@ -40,18 +38,17 @@ public:
virtual void Reset();
virtual int Assemble(
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
virtual int Assemble(runtime::FunctionInfo* symbol_info,
hir::HIRBuilder* builder, uint32_t debug_info_flags,
std::unique_ptr<runtime::DebugInfo> debug_info,
uint32_t trace_flags,
runtime::Function** out_function) = 0;
protected:
Backend* backend_;
};
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_ASSEMBLER_H_

View File

@ -7,30 +7,24 @@
******************************************************************************
*/
#include <alloy/backend/backend.h>
#include "alloy/backend/backend.h"
#include <alloy/backend/tracing.h>
namespace alloy {
namespace backend {
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::runtime;
using alloy::runtime::Runtime;
Backend::Backend(Runtime* runtime) :
runtime_(runtime) {
xe_zero_struct(&machine_info_, sizeof(machine_info_));
Backend::Backend(Runtime* runtime) : runtime_(runtime) {
memset(&machine_info_, 0, sizeof(machine_info_));
}
Backend::~Backend() {
}
Backend::~Backend() = default;
int Backend::Initialize() {
return 0;
}
int Backend::Initialize() { return 0; }
void* Backend::AllocThreadData() {
return NULL;
}
void* Backend::AllocThreadData() { return nullptr; }
void Backend::FreeThreadData(void* thread_data) {
}
void Backend::FreeThreadData(void* thread_data) {}
} // namespace backend
} // namespace alloy

View File

@ -10,18 +10,21 @@
#ifndef ALLOY_BACKEND_BACKEND_H_
#define ALLOY_BACKEND_BACKEND_H_
#include <alloy/core.h>
#include <alloy/backend/machine_info.h>
#include <memory>
#include "alloy/backend/machine_info.h"
namespace alloy { namespace runtime { class Runtime; } }
namespace alloy {
namespace runtime {
class Runtime;
} // namespace runtime
} // namespace alloy
namespace alloy {
namespace backend {
class Assembler;
class Backend {
public:
Backend(runtime::Runtime* runtime);
@ -35,16 +38,14 @@ public:
virtual void* AllocThreadData();
virtual void FreeThreadData(void* thread_data);
virtual Assembler* CreateAssembler() = 0;
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
protected:
runtime::Runtime* runtime_;
MachineInfo machine_info_;
};
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_BACKEND_H_

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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',
],
}

View File

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

View File

@ -10,13 +10,11 @@
#ifndef ALLOY_BACKEND_MACHINE_INFO_H_
#define ALLOY_BACKEND_MACHINE_INFO_H_
#include <alloy/core.h>
#include <cstdint>
namespace alloy {
namespace backend {
struct MachineInfo {
struct RegisterSet {
enum Types {
@ -31,9 +29,7 @@ struct MachineInfo {
} register_sets[8];
};
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_MACHINE_INFO_H_

View File

@ -6,11 +6,9 @@
'backend.cc',
'backend.h',
'machine_info.h',
'tracing.h',
],
'includes': [
'ivm/sources.gypi',
'x64/sources.gypi',
],
}

View File

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

View File

@ -1,12 +1,10 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'tracing.h',
'x64_assembler.cc',
'x64_assembler.h',
'x64_backend.cc',
'x64_backend.h',
'x64_code_cache.cc',
'x64_code_cache.h',
'x64_emitter.cc',
'x64_emitter.h',
@ -20,4 +18,17 @@
'x64_tracers.cc',
'x64_tracers.h',
],
'conditions': [
['OS == "mac" or OS == "linux"', {
'sources': [
'x64_code_cache_posix.cc',
],
}],
['OS == "win"', {
'sources': [
'x64_code_cache_win.cc',
],
}],
],
}

View File

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

View File

@ -7,39 +7,40 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_assembler.h>
#include "alloy/backend/x64/x64_assembler.h"
#include <alloy/backend/x64/tracing.h>
#include <alloy/backend/x64/x64_backend.h>
#include <alloy/backend/x64/x64_emitter.h>
#include <alloy/backend/x64/x64_function.h>
#include <alloy/hir/hir_builder.h>
#include <alloy/hir/label.h>
#include <alloy/runtime/runtime.h>
#include "alloy/reset_scope.h"
#include "alloy/backend/x64/x64_backend.h"
#include "alloy/backend/x64/x64_emitter.h"
#include "alloy/backend/x64/x64_function.h"
#include "alloy/hir/hir_builder.h"
#include "alloy/hir/label.h"
#include "alloy/runtime/runtime.h"
#include "xenia/profiling.h"
namespace BE {
#include <beaengine/BeaEngine.h>
}
} // namespace BE
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
using namespace alloy::hir;
namespace alloy {
namespace backend {
namespace x64 {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::runtime;
using alloy::hir::HIRBuilder;
using alloy::runtime::DebugInfo;
using alloy::runtime::Function;
using alloy::runtime::FunctionInfo;
X64Assembler::X64Assembler(X64Backend* backend) :
x64_backend_(backend),
emitter_(0), allocator_(0),
Assembler(backend) {
}
X64Assembler::X64Assembler(X64Backend* backend)
: Assembler(backend), x64_backend_(backend) {}
X64Assembler::~X64Assembler() {
alloy::tracing::WriteEvent(EventType::AssemblerDeinit({
}));
delete emitter_;
delete allocator_;
// Emitter must be freed before the allocator.
emitter_.reset();
allocator_.reset();
}
int X64Assembler::Initialize() {
@ -48,11 +49,8 @@ int X64Assembler::Initialize() {
return result;
}
allocator_ = new XbyakAllocator();
emitter_ = new X64Emitter(x64_backend_, allocator_);
alloy::tracing::WriteEvent(EventType::AssemblerInit({
}));
allocator_.reset(new XbyakAllocator());
emitter_.reset(new X64Emitter(x64_backend_, allocator_.get()));
return result;
}
@ -62,50 +60,45 @@ void X64Assembler::Reset() {
Assembler::Reset();
}
int X64Assembler::Assemble(
FunctionInfo* symbol_info, HIRBuilder* builder,
uint32_t debug_info_flags, DebugInfo* debug_info,
Function** out_function) {
int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
uint32_t trace_flags, Function** out_function) {
SCOPE_profile_cpu_f("alloy");
int result = 0;
// Reset when we leave.
make_reset_scope(this);
// Lower HIR -> x64.
void* machine_code = 0;
size_t code_size = 0;
result = emitter_->Emit(builder,
debug_info_flags, debug_info,
machine_code, code_size);
XEEXPECTZERO(result);
int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(),
trace_flags, machine_code, code_size);
if (result) {
return result;
}
// Stash generated machine code.
if (debug_info_flags & DEBUG_INFO_MACHINE_CODE_DISASM) {
DumpMachineCode(debug_info, machine_code, code_size, &string_buffer_);
if (debug_info_flags & DebugInfoFlags::DEBUG_INFO_MACHINE_CODE_DISASM) {
DumpMachineCode(debug_info.get(), machine_code, code_size, &string_buffer_);
debug_info->set_machine_code_disasm(string_buffer_.ToString());
string_buffer_.Reset();
}
{
X64Function* fn = new X64Function(symbol_info);
fn->set_debug_info(debug_info);
fn->set_debug_info(std::move(debug_info));
fn->Setup(machine_code, code_size);
*out_function = fn;
result = 0;
}
XECLEANUP:
Reset();
return result;
return 0;
}
void X64Assembler::DumpMachineCode(
DebugInfo* debug_info,
void* machine_code, size_t code_size,
StringBuffer* str) {
BE::DISASM disasm;
xe_zero_struct(&disasm, sizeof(disasm));
void X64Assembler::DumpMachineCode(DebugInfo* debug_info, void* machine_code,
size_t code_size, StringBuffer* str) {
BE::DISASM disasm = {0};
disasm.Archi = 64;
disasm.Options = BE::Tabulation + BE::MasmSyntax + BE::PrefixedNumeral;
disasm.EIP = (BE::UIntPtr)machine_code;
@ -113,8 +106,8 @@ void X64Assembler::DumpMachineCode(
uint64_t prev_source_offset = 0;
while (disasm.EIP < eip_end) {
// Look up source offset.
auto map_entry = debug_info->LookupCodeOffset(
disasm.EIP - (BE::UIntPtr)machine_code);
auto map_entry =
debug_info->LookupCodeOffset(disasm.EIP - (BE::UIntPtr)machine_code);
if (map_entry) {
if (map_entry->source_offset == prev_source_offset) {
str->Append(" ");
@ -134,3 +127,7 @@ void X64Assembler::DumpMachineCode(
disasm.EIP += len;
}
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -10,10 +10,10 @@
#ifndef ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
#define ALLOY_BACKEND_X64_X64_ASSEMBLER_H_
#include <alloy/core.h>
#include <alloy/backend/assembler.h>
#include <memory>
#include "alloy/backend/assembler.h"
#include "alloy/string_buffer.h"
namespace alloy {
namespace backend {
@ -23,38 +23,34 @@ class X64Backend;
class X64Emitter;
class XbyakAllocator;
class X64Assembler : public Assembler {
public:
X64Assembler(X64Backend* backend);
virtual ~X64Assembler();
~X64Assembler() override;
virtual int Initialize();
int Initialize() override;
virtual void Reset();
void Reset() override;
virtual int Assemble(
runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
runtime::Function** out_function);
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<runtime::DebugInfo> debug_info,
uint32_t trace_flags, runtime::Function** out_function) override;
private:
void DumpMachineCode(runtime::DebugInfo* debug_info,
void* machine_code, size_t code_size,
StringBuffer* str);
void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code,
size_t code_size, StringBuffer* str);
private:
X64Backend* x64_backend_;
X64Emitter* emitter_;
XbyakAllocator* allocator_;
std::unique_ptr<X64Emitter> emitter_;
std::unique_ptr<XbyakAllocator> allocator_;
StringBuffer string_buffer_;
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_ASSEMBLER_H_

View File

@ -7,30 +7,22 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_backend.h>
#include "alloy/backend/x64/x64_backend.h"
#include <alloy/backend/x64/tracing.h>
#include <alloy/backend/x64/x64_assembler.h>
#include <alloy/backend/x64/x64_code_cache.h>
#include <alloy/backend/x64/x64_sequences.h>
#include <alloy/backend/x64/x64_thunk_emitter.h>
#include "alloy/backend/x64/x64_assembler.h"
#include "alloy/backend/x64/x64_code_cache.h"
#include "alloy/backend/x64/x64_sequences.h"
#include "alloy/backend/x64/x64_thunk_emitter.h"
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
using namespace alloy::runtime;
namespace alloy {
namespace backend {
namespace x64 {
using alloy::runtime::Runtime;
X64Backend::X64Backend(Runtime* runtime) :
code_cache_(0),
Backend(runtime) {
}
X64Backend::X64Backend(Runtime* runtime) : Backend(runtime), code_cache_(0) {}
X64Backend::~X64Backend() {
alloy::tracing::WriteEvent(EventType::Deinit({
}));
delete code_cache_;
}
X64Backend::~X64Backend() { delete code_cache_; }
int X64Backend::Initialize() {
int result = Backend::Initialize();
@ -41,15 +33,10 @@ int X64Backend::Initialize() {
RegisterSequences();
machine_info_.register_sets[0] = {
0,
"gpr",
MachineInfo::RegisterSet::INT_TYPES,
X64Emitter::GPR_COUNT,
0, "gpr", MachineInfo::RegisterSet::INT_TYPES, X64Emitter::GPR_COUNT,
};
machine_info_.register_sets[1] = {
1,
"xmm",
MachineInfo::RegisterSet::FLOAT_TYPES |
1, "xmm", MachineInfo::RegisterSet::FLOAT_TYPES |
MachineInfo::RegisterSet::VEC_TYPES,
X64Emitter::XMM_COUNT,
};
@ -60,19 +47,19 @@ int X64Backend::Initialize() {
return result;
}
auto allocator = new XbyakAllocator();
auto thunk_emitter = new X64ThunkEmitter(this, allocator);
// Generate thunks used to transition between jitted code and host code.
auto allocator = std::make_unique<XbyakAllocator>();
auto thunk_emitter = std::make_unique<X64ThunkEmitter>(this, allocator.get());
host_to_guest_thunk_ = thunk_emitter->EmitHostToGuestThunk();
guest_to_host_thunk_ = thunk_emitter->EmitGuestToHostThunk();
delete thunk_emitter;
delete allocator;
alloy::tracing::WriteEvent(EventType::Init({
}));
return result;
}
Assembler* X64Backend::CreateAssembler() {
return new X64Assembler(this);
std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
return std::make_unique<X64Assembler>(this);
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -10,10 +10,7 @@
#ifndef ALLOY_BACKEND_X64_X64_BACKEND_H_
#define ALLOY_BACKEND_X64_X64_BACKEND_H_
#include <alloy/core.h>
#include <alloy/backend/backend.h>
#include "alloy/backend/backend.h"
namespace alloy {
namespace backend {
@ -21,25 +18,23 @@ namespace x64 {
class X64CodeCache;
#define ALLOY_HAS_X64_BACKEND 1
typedef void* (*HostToGuestThunk)(void* target, void* arg0, void* arg1);
typedef void* (*GuestToHostThunk)(void* target, void* arg0, void* arg1);
class X64Backend : public Backend {
public:
X64Backend(runtime::Runtime* runtime);
virtual ~X64Backend();
~X64Backend() override;
X64CodeCache* code_cache() const { return code_cache_; }
HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; }
GuestToHostThunk guest_to_host_thunk() const { return guest_to_host_thunk_; }
virtual int Initialize();
int Initialize() override;
virtual Assembler* CreateAssembler();
std::unique_ptr<Assembler> CreateAssembler() override;
private:
X64CodeCache* code_cache_;
@ -47,10 +42,8 @@ private:
GuestToHostThunk guest_to_host_thunk_;
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_BACKEND_H_

View File

@ -10,8 +10,7 @@
#ifndef ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
#define ALLOY_BACKEND_X64_X64_CODE_CACHE_H_
#include <alloy/core.h>
#include <mutex>
namespace alloy {
namespace backend {
@ -34,16 +33,14 @@ public:
private:
const static size_t DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024;
Mutex* lock_;
std::mutex lock_;
size_t chunk_size_;
X64CodeChunk* head_chunk_;
X64CodeChunk* active_chunk_;
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_CODE_CACHE_H_

View File

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

View File

@ -7,14 +7,10 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_code_cache.h>
#include <alloy/backend/x64/tracing.h>
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
#include "alloy/backend/x64/x64_code_cache.h"
#include "poly/poly.h"
#include "xenia/profiling.h"
namespace alloy {
namespace backend {
@ -24,6 +20,7 @@ class X64CodeChunk {
public:
X64CodeChunk(size_t chunk_size);
~X64CodeChunk();
public:
X64CodeChunk* next;
size_t capacity;
@ -44,20 +41,11 @@ public:
void AddTableEntry(uint8_t* code, size_t code_size, size_t stack_size);
};
} // namespace x64
} // namespace backend
} // namespace alloy
X64CodeCache::X64CodeCache(size_t chunk_size) :
chunk_size_(chunk_size),
head_chunk_(NULL), active_chunk_(NULL) {
lock_ = AllocMutex();
}
X64CodeCache::X64CodeCache(size_t chunk_size)
: chunk_size_(chunk_size), head_chunk_(NULL), active_chunk_(NULL) {}
X64CodeCache::~X64CodeCache() {
LockMutex(lock_);
std::lock_guard<std::mutex> guard(lock_);
auto chunk = head_chunk_;
while (chunk) {
auto next = chunk->next;
@ -65,32 +53,30 @@ X64CodeCache::~X64CodeCache() {
chunk = next;
}
head_chunk_ = NULL;
UnlockMutex(lock_);
FreeMutex(lock_);
}
int X64CodeCache::Initialize() {
return 0;
}
int X64CodeCache::Initialize() { return 0; }
void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
size_t stack_size) {
SCOPE_profile_cpu_f("alloy");
size_t alloc_size = code_size;
// Add unwind info into the allocation size. Keep things 16b aligned.
code_size += XEROUNDUP(X64CodeChunk::UNWIND_INFO_SIZE, 16);
alloc_size += poly::round_up(X64CodeChunk::UNWIND_INFO_SIZE, 16);
// Always move the code to land on 16b alignment. We do this by rounding up
// to 16b so that all offsets are aligned.
code_size = XEROUNDUP(code_size, 16);
alloc_size = poly::round_up(alloc_size, 16);
LockMutex(lock_);
lock_.lock();
if (active_chunk_) {
if (active_chunk_->capacity - active_chunk_->offset < code_size) {
if (active_chunk_->capacity - active_chunk_->offset < alloc_size) {
auto next = active_chunk_->next;
if (!next) {
XEASSERT(code_size < chunk_size_); // need to support larger chunks
assert_true(alloc_size < chunk_size_, "need to support larger chunks");
next = new X64CodeChunk(chunk_size_);
active_chunk_->next = next;
}
@ -101,40 +87,35 @@ void* X64CodeCache::PlaceCode(void* machine_code, size_t code_size,
}
uint8_t* final_address = active_chunk_->buffer + active_chunk_->offset;
active_chunk_->offset += code_size;
active_chunk_->offset += alloc_size;
// Add entry to fn table.
active_chunk_->AddTableEntry(final_address, code_size, stack_size);
active_chunk_->AddTableEntry(final_address, alloc_size, stack_size);
UnlockMutex(lock_);
lock_.unlock();
// Copy code.
xe_copy_struct(final_address, machine_code, code_size);
memcpy(final_address, machine_code, code_size);
// This isn't needed on x64 (probably), but is convention.
FlushInstructionCache(GetCurrentProcess(), final_address, code_size);
FlushInstructionCache(GetCurrentProcess(), final_address, alloc_size);
return final_address;
}
X64CodeChunk::X64CodeChunk(size_t chunk_size) :
next(NULL),
capacity(chunk_size), buffer(0), offset(0) {
buffer = (uint8_t*)VirtualAlloc(
NULL, capacity,
MEM_RESERVE | MEM_COMMIT,
X64CodeChunk::X64CodeChunk(size_t chunk_size)
: next(NULL), capacity(chunk_size), buffer(0), offset(0) {
buffer = (uint8_t*)VirtualAlloc(NULL, capacity, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
fn_table_capacity = (uint32_t)XEROUNDUP(capacity / ESTIMATED_FN_SIZE, 16);
fn_table_capacity =
static_cast<uint32_t>(poly::round_up(capacity / ESTIMATED_FN_SIZE, 16));
size_t table_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
fn_table = (RUNTIME_FUNCTION*)xe_malloc(table_size);
fn_table = (RUNTIME_FUNCTION*)malloc(table_size);
fn_table_count = 0;
fn_table_handle = 0;
RtlAddGrowableFunctionTable(
&fn_table_handle,
fn_table,
fn_table_count,
fn_table_capacity,
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
fn_table_capacity, (ULONG_PTR)buffer,
(ULONG_PTR)buffer + capacity);
}
X64CodeChunk::~X64CodeChunk() {
@ -214,33 +195,27 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
if (fn_table_count + 1 > fn_table_capacity) {
// Table exhausted, need to realloc. If this happens a lot we should tune
// the table size to prevent this.
XELOGW("X64CodeCache growing FunctionTable - adjust ESTIMATED_FN_SIZE");
PLOGW("X64CodeCache growing FunctionTable - adjust ESTIMATED_FN_SIZE");
RtlDeleteGrowableFunctionTable(fn_table_handle);
size_t old_size = fn_table_capacity * sizeof(RUNTIME_FUNCTION);
size_t new_size = old_size * 2;
auto new_table = (RUNTIME_FUNCTION*)xe_realloc(fn_table, old_size, new_size);
XEASSERTNOTNULL(new_table);
auto new_table = (RUNTIME_FUNCTION*)realloc(fn_table, new_size);
assert_not_null(new_table);
if (!new_table) {
return;
}
fn_table = new_table;
fn_table_capacity *= 2;
RtlAddGrowableFunctionTable(
&fn_table_handle,
fn_table,
fn_table_count,
fn_table_capacity,
(ULONG_PTR)buffer, (ULONG_PTR)buffer + capacity);
RtlAddGrowableFunctionTable(&fn_table_handle, fn_table, fn_table_count,
fn_table_capacity, (ULONG_PTR)buffer,
(ULONG_PTR)buffer + capacity);
}
// Allocate unwind data. We know we have space because we overallocated.
// This should be the tailing 16b with 16b alignment.
size_t unwind_info_offset = offset;
offset += UNWIND_INFO_SIZE;
size_t unwind_info_offset = offset - UNWIND_INFO_SIZE;
if (!stack_size) {
uint8_t prolog_size = 0;
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
UNWIND_INFO* unwind_info = (UNWIND_INFO*)(buffer + unwind_info_offset);
unwind_info->Version = 1;
@ -264,7 +239,8 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
size_t co = 0;
auto& unwind_code = unwind_info->UnwindCode[co++];
unwind_code.CodeOffset = 14; // end of instruction + 1 == offset of next instruction
unwind_code.CodeOffset =
14; // end of instruction + 1 == offset of next instruction
unwind_code.UnwindOp = UWOP_ALLOC_SMALL;
unwind_code.OpInfo = stack_size / 8 - 1;
} else {
@ -283,7 +259,8 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
size_t co = 0;
auto& unwind_code = unwind_info->UnwindCode[co++];
unwind_code.CodeOffset = 7; // end of instruction + 1 == offset of next instruction
unwind_code.CodeOffset =
7; // end of instruction + 1 == offset of next instruction
unwind_code.UnwindOp = UWOP_ALLOC_LARGE;
unwind_code.OpInfo = 0;
unwind_code = unwind_info->UnwindCode[co++];
@ -299,3 +276,7 @@ void X64CodeChunk::AddTableEntry(uint8_t* code, size_t code_size,
// Notify the function table that it has new entries.
RtlGrowFunctionTable(fn_table_handle, fn_table_count);
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -7,75 +7,70 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_emitter.h>
#include <alloy/backend/x64/x64_backend.h>
#include <alloy/backend/x64/x64_code_cache.h>
#include <alloy/backend/x64/x64_function.h>
#include <alloy/backend/x64/x64_sequences.h>
#include <alloy/backend/x64/x64_thunk_emitter.h>
#include <alloy/hir/hir_builder.h>
#include <alloy/runtime/debug_info.h>
#include <alloy/runtime/runtime.h>
#include <alloy/runtime/symbol_info.h>
#include <alloy/runtime/thread_state.h>
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
using namespace alloy::hir;
using namespace alloy::runtime;
using namespace Xbyak;
#include "alloy/backend/x64/x64_emitter.h"
#include "alloy/alloy-private.h"
#include "alloy/backend/x64/x64_backend.h"
#include "alloy/backend/x64/x64_code_cache.h"
#include "alloy/backend/x64/x64_function.h"
#include "alloy/backend/x64/x64_sequences.h"
#include "alloy/backend/x64/x64_thunk_emitter.h"
#include "alloy/hir/hir_builder.h"
#include "alloy/runtime/debug_info.h"
#include "alloy/runtime/runtime.h"
#include "alloy/runtime/symbol_info.h"
#include "alloy/runtime/thread_state.h"
#include "xdb/protocol.h"
#include "xenia/profiling.h"
namespace alloy {
namespace backend {
namespace x64 {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using namespace alloy::runtime;
using namespace Xbyak;
using alloy::hir::HIRBuilder;
using alloy::hir::Instr;
using alloy::runtime::Function;
using alloy::runtime::FunctionInfo;
using alloy::runtime::SourceMapEntry;
using alloy::runtime::ThreadState;
static const size_t MAX_CODE_SIZE = 1 * 1024 * 1024;
static const size_t STASH_OFFSET = 32;
static const size_t STASH_OFFSET_HIGH = 32 + 32;
// If we are running with tracing on we have to store the EFLAGS in the stack,
// otherwise our calls out to C to print will clear it before DID_CARRY/etc
// can get the value.
#define STORE_EFLAGS 1
} // namespace x64
} // namespace backend
} // namespace alloy
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
Operand::RBX,
Operand::R12, Operand::R13, Operand::R14, Operand::R15,
Operand::RBX, Operand::R12, Operand::R13, Operand::R14, Operand::R15,
};
const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
};
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) :
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
: CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator),
runtime_(backend->runtime()),
backend_(backend),
code_cache_(backend->code_cache()),
allocator_(allocator),
current_instr_(0),
CodeGenerator(MAX_CODE_SIZE, AutoGrow, allocator) {
}
current_instr_(0) {}
X64Emitter::~X64Emitter() {
}
X64Emitter::~X64Emitter() {}
int X64Emitter::Initialize() {
return 0;
}
int X64Emitter::Initialize() { return 0; }
int X64Emitter::Emit(
HIRBuilder* builder,
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
runtime::DebugInfo* debug_info, uint32_t trace_flags,
void*& out_code_address, size_t& out_code_size) {
SCOPE_profile_cpu_f("alloy");
@ -84,6 +79,7 @@ int X64Emitter::Emit(
source_map_count_ = 0;
source_map_arena_.Reset();
}
trace_flags_ = trace_flags;
// Fill the generator with code.
size_t stack_size = 0;
@ -99,8 +95,7 @@ int X64Emitter::Emit(
// Stash source map.
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
debug_info->InitializeSourceMap(
source_map_count_,
(SourceMapEntry*)source_map_arena_.CloneContents());
source_map_count_, (SourceMapEntry*)source_map_arena_.CloneContents());
}
return 0;
@ -129,13 +124,13 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
auto slot = *it;
size_t type_size = GetTypeSize(slot->type);
// Align to natural size.
stack_offset = XEALIGN(stack_offset, type_size);
stack_offset = poly::align(stack_offset, type_size);
slot->set_constant((uint32_t)stack_offset);
stack_offset += type_size;
}
// Ensure 16b alignment.
stack_offset -= StackLayout::GUEST_STACK_SIZE;
stack_offset = XEALIGN(stack_offset, 16);
stack_offset = poly::align(stack_offset, static_cast<size_t>(16));
// Function prolog.
// Must be 16b aligned.
@ -150,7 +145,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
// Adding or changing anything here must be matched!
const bool emit_prolog = true;
const size_t stack_size = StackLayout::GUEST_STACK_SIZE + stack_offset;
XEASSERT((stack_size + 8) % 16 == 0);
assert_true((stack_size + 8) % 16 == 0);
out_stack_size = stack_size;
stack_size_ = stack_size;
if (emit_prolog) {
@ -161,6 +156,19 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
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.
auto block = builder->first_block();
while (block) {
@ -175,10 +183,20 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
const Instr* instr = block->instr_head;
while (instr) {
const Instr* new_tail = instr;
// Special handling of TRACE_SOURCE.
if (instr->opcode == &OPCODE_TRACE_SOURCE_info) {
if (trace_flags_ & TRACE_SOURCE) {
EmitTraceSource(instr);
}
instr = instr->next;
continue;
}
if (!SelectSequence(*this, instr, &new_tail)) {
// No sequence found!
XEASSERTALWAYS();
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
assert_always();
PLOGE("Unable to process HIR opcode %s", instr->opcode->name);
break;
}
instr = new_tail;
@ -189,6 +207,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
// Function epilog.
L("epilog");
EmitTraceUserCallReturn();
if (emit_prolog) {
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
add(rsp, (uint32_t)stack_size);
@ -214,21 +233,160 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
source_map_count_++;
}
void X64Emitter::EmitTraceSource(const Instr* instr) {
uint64_t trace_base = runtime_->memory()->trace_base();
if (!trace_base || !(trace_flags_ & TRACE_SOURCE)) {
return;
}
// TODO(benvanik): make this a function call to some append fn.
uint8_t dest_reg_0 = instr->flags & 0xFF;
uint8_t dest_reg_1 = instr->flags >> 8;
xdb::protocol::EventType event_type;
size_t event_size = 0;
if (dest_reg_0 == 100) {
event_type = xdb::protocol::EventType::INSTR;
event_size = sizeof(xdb::protocol::InstrEvent);
} else if (dest_reg_1 == 100) {
if (dest_reg_0 & (1 << 7)) {
event_type = xdb::protocol::EventType::INSTR_R16;
event_size = sizeof(xdb::protocol::InstrEventR16);
} else {
event_type = xdb::protocol::EventType::INSTR_R8;
event_size = sizeof(xdb::protocol::InstrEventR8);
}
} else {
if (dest_reg_0 & (1 << 7) && dest_reg_1 & (1 << 7)) {
event_type = xdb::protocol::EventType::INSTR_R16_R16;
event_size = sizeof(xdb::protocol::InstrEventR16R16);
} else if (dest_reg_0 & (1 << 7) && !(dest_reg_1 & (1 << 7))) {
event_type = xdb::protocol::EventType::INSTR_R16_R8;
event_size = sizeof(xdb::protocol::InstrEventR16R8);
} else if (!(dest_reg_0 & (1 << 7)) && dest_reg_1 & (1 << 7)) {
event_type = xdb::protocol::EventType::INSTR_R8_R16;
event_size = sizeof(xdb::protocol::InstrEventR8R16);
} else if (!(dest_reg_0 & (1 << 7)) && !(dest_reg_1 & (1 << 7))) {
event_type = xdb::protocol::EventType::INSTR_R8_R8;
event_size = sizeof(xdb::protocol::InstrEventR8R8);
}
}
assert_not_zero(event_size);
mov(rax, trace_base);
mov(r8d, static_cast<uint32_t>(event_size));
lock();
xadd(qword[rax], r8);
// r8 is now the pointer where we can write our event.
// Write the header, which is the same for everything (pretty much).
// Some event types ignore the dest reg, and that's fine.
uint64_t qword_0 = static_cast<uint64_t>(event_type) |
(static_cast<uint64_t>(dest_reg_0) << 8) |
(instr->src1.offset << 32);
mov(rax, qword_0);
mov(qword[r8], rax);
// Write thread ID.
EmitGetCurrentThreadId();
mov(word[r8 + 2], ax);
switch (event_type) {
default:
case xdb::protocol::EventType::INSTR:
break;
case xdb::protocol::EventType::INSTR_R8:
case xdb::protocol::EventType::INSTR_R16:
if (dest_reg_0 & (1 << 7)) {
EmitTraceSourceAppendValue(instr->src2.value, 8);
} else {
EmitTraceSourceAppendValue(instr->src2.value, 8);
}
break;
case xdb::protocol::EventType::INSTR_R8_R8:
case xdb::protocol::EventType::INSTR_R8_R16:
case xdb::protocol::EventType::INSTR_R16_R8:
case xdb::protocol::EventType::INSTR_R16_R16:
mov(word[r8 + 8], dest_reg_0 | static_cast<uint16_t>(dest_reg_1 << 8));
size_t offset = 8;
if (dest_reg_0 & (1 << 7)) {
EmitTraceSourceAppendValue(instr->src2.value, offset);
offset += 16;
} else {
EmitTraceSourceAppendValue(instr->src2.value, offset);
offset += 8;
}
if (dest_reg_1 & (1 << 7)) {
EmitTraceSourceAppendValue(instr->src3.value, offset);
offset += 16;
} else {
EmitTraceSourceAppendValue(instr->src3.value, offset);
offset += 8;
}
break;
}
}
void X64Emitter::EmitTraceSourceAppendValue(const Value* value,
size_t r8_offset) {
//
}
void X64Emitter::EmitGetCurrentThreadId() {
// rcx must point to context. We could fetch from the stack if needed.
mov(ax, word[rcx + runtime_->frontend()->context_info()->thread_id_offset()]);
}
void X64Emitter::EmitTraceUserCallReturn() {
auto trace_base = runtime_->memory()->trace_base();
if (!trace_base || !(trace_flags_ & TRACE_USER_CALLS)) {
return;
}
mov(rdx, rax);
mov(rax, trace_base);
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::UserCallReturnEvent)));
lock();
xadd(qword[rax], r8);
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::USER_CALL_RETURN) |
(static_cast<uint64_t>(0) << 8) | (0ull << 32));
mov(qword[r8], rax);
EmitGetCurrentThreadId();
mov(word[r8 + 2], ax);
mov(rax, rdx);
ReloadEDX();
}
void X64Emitter::DebugBreak() {
// TODO(benvanik): notify debugger.
db(0xCC);
}
void X64Emitter::Trap() {
void X64Emitter::Trap(uint16_t trap_type) {
switch (trap_type) {
case 20:
// 0x0FE00014 is a 'debug print' where r3 = buffer r4 = length
// TODO(benvanik): debug print at runtime.
break;
case 0:
case 22:
// Always trap?
// TODO(benvanik): post software interrupt to debugger.
if (FLAGS_break_on_debugbreak) {
db(0xCC);
}
break;
default:
PLOGW("Unknown trap type %d", trap_type);
db(0xCC);
break;
}
}
void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
// TODO(benvanik): notify debugger.
db(0xCC);
XEASSERTALWAYS();
assert_always();
}
// Total size of ResolveFunctionSymbol call site in bytes.
@ -236,17 +394,6 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
const size_t TOTAL_RESOLVE_SIZE = 27;
const size_t ASM_OFFSET = 2 + 2 + 8 + 2 + 8;
// Length Assembly Byte Sequence
// =================================================================================
// 2 bytes 66 NOP 66 90H
// 3 bytes NOP DWORD ptr [EAX] 0F 1F 00H
// 4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
// 5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
// 6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
// 7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
// 8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
// 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H
uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
// TODO(benvanik): generate this thunk at runtime? or a shim?
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
@ -255,13 +402,18 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
// Resolve function. This will demand compile as required.
Function* fn = NULL;
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
XEASSERTNOTNULL(fn);
assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn);
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
// Overwrite the call site.
// The return address points to ReloadRCX work after the call.
#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 mov_rax;
@ -272,6 +424,7 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
uint8_t mov_rcx[5];
};
#pragma pack(pop)
static_assert_size(Asm, TOTAL_RESOLVE_SIZE);
Asm* code = reinterpret_cast<Asm*>(return_address - ASM_OFFSET);
code->rax_constant = addr;
code->call_rax = 0x9066;
@ -280,10 +433,10 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
return addr;
}
void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info) {
void X64Emitter::Call(const hir::Instr* instr,
runtime::FunctionInfo* symbol_info) {
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
if (fn) {
mov(rax, reinterpret_cast<uint64_t>(fn->machine_code()));
} else {
@ -297,12 +450,15 @@ void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_inf
// 5b
ReloadECX();
size_t total_size = getSize() - start;
XEASSERT(total_size == TOTAL_RESOLVE_SIZE);
assert_true(total_size == TOTAL_RESOLVE_SIZE);
// EDX overwritten, don't bother reloading.
}
// Actually jump/call to rax.
if (instr->flags & CALL_TAIL) {
// Since we skip the prolog we need to mark the return here.
EmitTraceUserCallReturn();
// Pass the callers return address over.
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
@ -315,18 +471,66 @@ void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_inf
}
}
// NOTE: slot count limited by short jump size.
const int kICSlotCount = 4;
const int kICSlotSize = 23;
const uint64_t kICSlotInvalidTargetAddress = 0x0F0F0F0F0F0F0F0F;
uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
// TODO(benvanik): generate this thunk at runtime? or a shim?
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
// TODO(benvanik): required?
target_address &= 0xFFFFFFFF;
assert_not_zero(target_address);
Function* fn = NULL;
thread_state->runtime()->ResolveFunction(target_address, &fn);
XEASSERTNOTNULL(fn);
assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn);
return reinterpret_cast<uint64_t>(x64_fn->machine_code());
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
// Add an IC slot, if there is room.
#if XE_LIKE_WIN32
uint64_t return_address = reinterpret_cast<uint64_t>(_ReturnAddress());
#else
uint64_t return_address =
reinterpret_cast<uint64_t>(__builtin_return_address(0));
#endif // XE_LIKE_WIN32
#pragma pack(push, 1)
struct Asm {
uint16_t cmp_rdx;
uint32_t address_constant;
uint16_t jmp_next_slot;
uint16_t mov_rax;
uint64_t target_constant;
uint8_t jmp_skip_resolve[5];
};
#pragma pack(pop)
static_assert_size(Asm, kICSlotSize);
// The return address points to ReloadRCX work after the call.
// To get the top of the table, look back a ways.
uint64_t table_start = return_address - 12 - kICSlotSize * kICSlotCount;
// NOTE: order matters here - we update the address BEFORE we switch the code
// over to passing the compare.
Asm* table_slot = reinterpret_cast<Asm*>(table_start);
bool wrote_ic = false;
for (int i = 0; i < kICSlotCount; ++i) {
if (poly::atomic_cas(kICSlotInvalidTargetAddress, addr,
&table_slot->target_constant)) {
// Got slot! Just write the compare and we're done.
table_slot->address_constant = static_cast<uint32_t>(target_address);
wrote_ic = true;
break;
}
++table_slot;
}
if (!wrote_ic) {
// TODO(benvanik): log that IC table is full.
}
// We need to return the target in rax so that it gets called.
return addr;
}
void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
@ -336,15 +540,53 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
je("epilog", CodeGenerator::T_NEAR);
}
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
if (reg.getIdx() != rdx.getIdx()) {
mov(rdx, reg);
}
inLocalLabel();
Xbyak::Label skip_resolve;
// TODO(benvanik): make empty tables skippable (cmp, jump right to resolve).
// IC table, initially empty.
// This will get filled in as functions are resolved.
// Note that we only have a limited cache, and once it's full all calls
// will fall through.
// TODO(benvanik): check miss rate when full and add a 2nd-level table?
// 0000000264BD4DC3 81 FA 0F0F0F0F cmp edx,0F0F0F0Fh
// 0000000264BD4DC9 75 0C jne 0000000264BD4DD7
// 0000000264BD4DCB 48 B8 0F0F0F0F0F0F0F0F mov rax,0F0F0F0F0F0F0F0Fh
// 0000000264BD4DD5 EB XXXXXXXX jmp 0000000264BD4E00
size_t table_start = getSize();
for (int i = 0; i < kICSlotCount; ++i) {
// Compare target address with constant, if matches jump there.
// Otherwise, fall through.
// 6b
cmp(edx, 0x0F0F0F0F);
Xbyak::Label next_slot;
// 2b
jne(next_slot, T_SHORT);
// Match! Load up rax and skip down to the jmp code.
// 10b
mov(rax, kICSlotInvalidTargetAddress);
// 5b
jmp(skip_resolve, T_NEAR);
L(next_slot);
}
size_t table_size = getSize() - table_start;
assert_true(table_size == kICSlotSize * kICSlotCount);
// Resolve address to the function to call and store in rax.
// We fall through to this when there are no hits in the IC table.
CallNative(ResolveFunctionAddress);
// Actually jump/call to rax.
L(skip_resolve);
if (instr->flags & CALL_TAIL) {
// Since we skip the prolog we need to mark the return here.
EmitTraceUserCallReturn();
// Pass the callers return address over.
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
@ -355,17 +597,37 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
mov(rdx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
call(rax);
}
outLocalLabel();
}
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
XELOGW("undefined extern call to %.8X %s",
symbol_info->address(),
symbol_info->name());
PLOGW("undefined extern call to %.8llX %s", symbol_info->address(),
symbol_info->name().c_str());
return 0;
}
void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info) {
XEASSERT(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
void X64Emitter::CallExtern(const hir::Instr* instr,
const FunctionInfo* symbol_info) {
assert_true(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
uint64_t trace_base = runtime_->memory()->trace_base();
if (trace_base & trace_flags_ & TRACE_EXTERN_CALLS) {
mov(rax, trace_base);
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallEvent)));
lock();
xadd(qword[rax], r8);
// TODO(benvanik): get module/ordinal.
uint32_t module_id = 0;
uint32_t ordinal = 0;
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL) |
(static_cast<uint64_t>(0) << 8) | (module_id << 16) |
(ordinal));
mov(qword[r8], rax);
EmitGetCurrentThreadId();
mov(word[r8 + 2], ax);
}
if (!symbol_info->extern_handler()) {
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
} else {
@ -383,6 +645,19 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_
ReloadEDX();
// rax = host return
}
if (trace_base && trace_flags_ & TRACE_EXTERN_CALLS) {
mov(rax, trace_base);
mov(r8d,
static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallReturnEvent)));
lock();
xadd(qword[rax], r8);
mov(rax,
static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL_RETURN) |
(static_cast<uint64_t>(0) << 8) | (0));
mov(qword[r8], rax);
EmitGetCurrentThreadId();
mov(word[r8 + 2], ax);
}
}
void X64Emitter::CallNative(void* fn) {
@ -406,7 +681,8 @@ void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0)) {
ReloadEDX();
}
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0) {
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
uint64_t arg0) {
mov(rdx, arg0);
mov(rax, reinterpret_cast<uint64_t>(fn));
call(rax);
@ -440,11 +716,27 @@ void X64Emitter::ReloadEDX() {
mov(rdx, qword[rcx + 8]); // membase
}
// Len Assembly Byte Sequence
// ============================================================================
// 2b 66 NOP 66 90H
// 3b NOP DWORD ptr [EAX] 0F 1F 00H
// 4b NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
// 5b NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
// 6b 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
// 7b NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
// 8b NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
// 9b 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H
void X64Emitter::nop(size_t length) {
// TODO(benvanik): fat nop
for (size_t i = 0; i < length; ++i) {
db(0x90);
}
}
void X64Emitter::LoadEflags() {
#if STORE_EFLAGS
mov(eax, dword[rsp + STASH_OFFSET]);
push(rax);
popf();
btr(eax, 0);
#else
// EFLAGS already present.
#endif // STORE_EFLAGS
@ -453,18 +745,13 @@ void X64Emitter::LoadEflags() {
void X64Emitter::StoreEflags() {
#if STORE_EFLAGS
pushf();
pop(qword[rsp + STASH_OFFSET]);
pop(dword[rsp + STASH_OFFSET]);
#else
// EFLAGS should have CA set?
// (so long as we don't fuck with it)
#endif // STORE_EFLAGS
}
uint32_t X64Emitter::page_table_address() const {
uint64_t addr = runtime_->memory()->page_table();
return static_cast<uint32_t>(addr);
}
bool X64Emitter::ConstantFitsIn32Reg(uint64_t v) {
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
@ -498,32 +785,79 @@ void X64Emitter::MovMem64(const RegExp& addr, uint64_t v) {
Address X64Emitter::GetXmmConstPtr(XmmConst id) {
static const vec128_t xmm_consts[] = {
/* XMMZero */ vec128f(0.0f, 0.0f, 0.0f, 0.0f),
/* XMMOne */ vec128f(1.0f, 1.0f, 1.0f, 1.0f),
/* XMMZero */ vec128f(0.0f),
/* XMMOne */ vec128f(1.0f),
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u, 0x00000000u, 0x00000000u),
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u, 0x00000000u, 0x00000000u),
/* XMMFFFF */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
0xFFFFFFFFu, 0xFFFFFFFFu),
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u,
0x00000000u, 0x00000000u),
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u,
0x00000000u, 0x00000000u),
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
/* XMMNormalizeX16Y16 */ vec128f(1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
/* XMMNormalizeX16Y16 */ vec128f(
1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u),
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu),
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu, 0xFFFFFFFFu, 0x7FFFFFFFu),
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu),
/* XMMPermuteControl15 */ vec128b(15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15),
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x0C000408u),
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du, 0xFFFFFF0Cu, 0xFFFFFF0Fu),
/* XMMOneOver255 */ vec128f(1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f),
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu, 0x0000001Fu),
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu, 0x000000FFu, 0x000000FFu),
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0x00000000u),
/* XMM255 */ vec128f(255.0f, 255.0f, 255.0f, 255.0f),
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u, 0x80808080u, 0x80808080u),
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u, 0x80008000u, 0x80008000u),
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
/* XMM3333 */ vec128f(3.0f, 3.0f, 3.0f, 3.0f),
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u,
0x80000000u, 0x80000000u),
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u,
0x00000000u, 0x80000000u),
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu,
0x7FFFFFFFu, 0x7FFFFFFFu),
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu,
0xFFFFFFFFu, 0x7FFFFFFFu),
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u,
0x08090A0Bu, 0x0C0D0E0Fu),
/* XMMByteOrderMask */ vec128i(0x01000302u, 0x05040706u,
0x09080B0Au, 0x0D0C0F0Eu),
/* XMMPermuteControl15 */ vec128b(15),
/* XMMPermuteByteMask */ vec128b(0x1F),
/* XMMPackD3DCOLORSat */ vec128i(0x404000FFu),
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
0xFFFFFFFFu, 0x0C000408u),
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du,
0xFFFFFF0Cu, 0xFFFFFF0Fu),
/* XMMPackFLOAT16_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
0xFFFFFFFFu, 0x01000302u),
/* XMMUnpackFLOAT16_2 */ vec128i(0x0D0C0F0Eu, 0xFFFFFFFFu,
0xFFFFFFFFu, 0xFFFFFFFFu),
/* XMMPackFLOAT16_4 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
0x05040706u, 0x01000302u),
/* XMMUnpackFLOAT16_4 */ vec128i(0x09080B0Au, 0x0D0C0F0Eu,
0xFFFFFFFFu, 0xFFFFFFFFu),
/* XMMPackSHORT_2Min */ vec128i(0x403F8001u),
/* XMMPackSHORT_2Max */ vec128i(0x40407FFFu),
/* XMMPackSHORT_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu,
0xFFFFFFFFu, 0x01000504u),
/* XMMUnpackSHORT_2 */ vec128i(0xFFFF0F0Eu, 0xFFFF0D0Cu,
0xFFFFFFFFu, 0xFFFFFFFFu),
/* XMMOneOver255 */ vec128f(1.0f / 255.0f),
/* XMMMaskEvenPI16 */ vec128i(0x0000FFFFu, 0x0000FFFFu,
0x0000FFFFu, 0x0000FFFFu),
/* XMMShiftMaskEvenPI16 */ vec128i(0x0000000Fu, 0x0000000Fu,
0x0000000Fu, 0x0000000Fu),
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu,
0x0000001Fu, 0x0000001Fu),
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu,
0x000000FFu, 0x000000FFu),
/* XMMSwapWordMask */ vec128i(0x03030303u, 0x03030303u,
0x03030303u, 0x03030303u),
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u,
0xFFFFFFFFu, 0x00000000u),
/* XMM255 */ vec128f(255.0f),
/* XMMPI32 */ vec128i(32),
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u,
0x80808080u, 0x80808080u),
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u,
0x80008000u, 0x80008000u),
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u,
0x80000000u, 0x80000000u),
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u,
0x80000000u, 0x80000000u),
/* XMMShortMinPS */ vec128f(SHRT_MIN),
/* XMMShortMaxPS */ vec128f(SHRT_MAX),
};
// TODO(benvanik): cache base pointer somewhere? stack? It'd be nice to
// prevent this move.
@ -559,7 +893,7 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, float v) {
if (!v) {
// 0
vpxor(dest, dest);
} else if (x.i == ~0UL) {
} else if (x.i == ~0U) {
// 1111...
vpcmpeqb(dest, dest);
} else {
@ -589,15 +923,12 @@ void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, double v) {
}
}
Address X64Emitter::StashXmm(const Xmm& r) {
auto addr = ptr[rsp + STASH_OFFSET];
Address X64Emitter::StashXmm(int index, const Xmm& r) {
auto addr = ptr[rsp + STASH_OFFSET + (index * 16)];
vmovups(addr, r);
return addr;
}
Address X64Emitter::StashXmm(const vec128_t& v) {
auto addr = ptr[rsp + STASH_OFFSET];
LoadConstantXmm(xmm0, v);
vmovups(addr, xmm0);
return addr;
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -10,18 +10,21 @@
#ifndef ALLOY_BACKEND_X64_X64_EMITTER_H_
#define ALLOY_BACKEND_X64_X64_EMITTER_H_
#include <alloy/core.h>
#include "alloy/hir/value.h"
#include "third_party/xbyak/xbyak/xbyak.h"
#include <alloy/hir/value.h>
#include <third_party/xbyak/xbyak/xbyak.h>
XEDECLARECLASS2(alloy, hir, HIRBuilder);
XEDECLARECLASS2(alloy, hir, Instr);
XEDECLARECLASS2(alloy, runtime, DebugInfo);
XEDECLARECLASS2(alloy, runtime, FunctionInfo);
XEDECLARECLASS2(alloy, runtime, Runtime);
XEDECLARECLASS2(alloy, runtime, SymbolInfo);
namespace alloy {
namespace hir {
class HIRBuilder;
class Instr;
} // namespace hir
namespace runtime {
class DebugInfo;
class FunctionInfo;
class Runtime;
class SymbolInfo;
} // namespace runtime
} // namespace alloy
namespace alloy {
namespace backend {
@ -39,29 +42,48 @@ enum XmmConst {
XMMZero = 0,
XMMOne,
XMMNegativeOne,
XMMFFFF,
XMMMaskX16Y16,
XMMFlipX16Y16,
XMMFixX16Y16,
XMMNormalizeX16Y16,
XMM0001,
XMM3301,
XMM3333,
XMMSignMaskPS,
XMMSignMaskPD,
XMMAbsMaskPS,
XMMAbsMaskPD,
XMMByteSwapMask,
XMMByteOrderMask,
XMMPermuteControl15,
XMMPermuteByteMask,
XMMPackD3DCOLORSat,
XMMPackD3DCOLOR,
XMMUnpackD3DCOLOR,
XMMPackFLOAT16_2,
XMMUnpackFLOAT16_2,
XMMPackFLOAT16_4,
XMMUnpackFLOAT16_4,
XMMPackSHORT_2Min,
XMMPackSHORT_2Max,
XMMPackSHORT_2,
XMMUnpackSHORT_2,
XMMOneOver255,
XMMMaskEvenPI16,
XMMShiftMaskEvenPI16,
XMMShiftMaskPS,
XMMShiftByteMask,
XMMSwapWordMask,
XMMUnsignedDwordMax,
XMM255,
XMMPI32,
XMMSignMaskI8,
XMMSignMaskI16,
XMMSignMaskI32,
XMMSignMaskF32,
XMMShortMinPS,
XMMShortMaxPS,
};
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
@ -80,8 +102,8 @@ public:
int Initialize();
int Emit(hir::HIRBuilder* builder,
uint32_t debug_info_flags, runtime::DebugInfo* debug_info,
int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
runtime::DebugInfo* debug_info, uint32_t trace_flags,
void*& out_code_address, size_t& out_code_size);
public:
@ -117,29 +139,31 @@ public:
void MarkSourceOffset(const hir::Instr* i);
void DebugBreak();
void Trap();
void Trap(uint16_t trap_type = 0);
void UnimplementedInstr(const hir::Instr* i);
void UnimplementedExtern(const hir::Instr* i);
void Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info);
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
void CallExtern(const hir::Instr* instr, const runtime::FunctionInfo* symbol_info);
void CallExtern(const hir::Instr* instr,
const runtime::FunctionInfo* symbol_info);
void CallNative(void* fn);
void CallNative(uint64_t (*fn)(void* raw_context));
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0));
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0);
void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
uint64_t arg0);
void CallNativeSafe(void* fn);
void SetReturnAddress(uint64_t value);
void ReloadECX();
void ReloadEDX();
void nop(size_t length = 1);
// TODO(benvanik): Label for epilog (don't use strings).
void LoadEflags();
void StoreEflags();
uint32_t page_table_address() const;
// Moves a 64bit immediate into memory.
bool ConstantFitsIn32Reg(uint64_t v);
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
@ -148,14 +172,17 @@ public:
void LoadConstantXmm(Xbyak::Xmm dest, float v);
void LoadConstantXmm(Xbyak::Xmm dest, double v);
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
Xbyak::Address StashXmm(const Xbyak::Xmm& r);
Xbyak::Address StashXmm(const vec128_t& v);
Xbyak::Address StashXmm(int index, const Xbyak::Xmm& r);
size_t stack_size() const { return stack_size_; }
protected:
void* Emplace(size_t stack_size);
int Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
void EmitTraceSource(const hir::Instr* instr);
void EmitTraceSourceAppendValue(const hir::Value* value, size_t r8_offset);
void EmitGetCurrentThreadId();
void EmitTraceUserCallReturn();
protected:
runtime::Runtime* runtime_;
@ -170,14 +197,14 @@ protected:
size_t stack_size_;
uint32_t trace_flags_;
static const uint32_t gpr_reg_map_[GPR_COUNT];
static const uint32_t xmm_reg_map_[XMM_COUNT];
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_EMITTER_H_

View File

@ -7,23 +7,23 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_function.h>
#include "alloy/backend/x64/x64_function.h"
#include <alloy/backend/x64/tracing.h>
#include <alloy/backend/x64/x64_backend.h>
#include <alloy/runtime/runtime.h>
#include <alloy/runtime/thread_state.h>
#include "alloy/backend/x64/x64_backend.h"
#include "alloy/runtime/runtime.h"
#include "alloy/runtime/thread_state.h"
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
using namespace alloy::runtime;
namespace alloy {
namespace backend {
namespace x64 {
using alloy::runtime::Breakpoint;
using alloy::runtime::Function;
using alloy::runtime::FunctionInfo;
using alloy::runtime::ThreadState;
X64Function::X64Function(FunctionInfo* symbol_info) :
machine_code_(NULL), code_size_(0),
Function(symbol_info) {
}
X64Function::X64Function(FunctionInfo* symbol_info)
: Function(symbol_info), machine_code_(nullptr), code_size_(0) {}
X64Function::~X64Function() {
// machine_code_ is freed by code cache.
@ -34,20 +34,17 @@ void X64Function::Setup(void* machine_code, size_t code_size) {
code_size_ = code_size;
}
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) {
return 0;
}
int X64Function::AddBreakpointImpl(Breakpoint* breakpoint) { return 0; }
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) {
return 0;
}
int X64Function::RemoveBreakpointImpl(Breakpoint* breakpoint) { return 0; }
int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
auto backend = (X64Backend*)thread_state->runtime()->backend();
auto thunk = backend->host_to_guest_thunk();
thunk(
machine_code_,
thread_state->raw_context(),
(void*)return_address);
thunk(machine_code_, thread_state->raw_context(), (void*)return_address);
return 0;
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -10,16 +10,13 @@
#ifndef ALLOY_BACKEND_X64_X64_FUNCTION_H_
#define ALLOY_BACKEND_X64_X64_FUNCTION_H_
#include <alloy/core.h>
#include <alloy/runtime/function.h>
#include <alloy/runtime/symbol_info.h>
#include "alloy/runtime/function.h"
#include "alloy/runtime/symbol_info.h"
namespace alloy {
namespace backend {
namespace x64 {
class X64Function : public runtime::Function {
public:
X64Function(runtime::FunctionInfo* symbol_info);
@ -41,10 +38,8 @@ private:
size_t code_size_;
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_FUNCTION_H_

View File

@ -133,7 +133,7 @@ struct ValueOp : Op<ValueOp<T, KEY_TYPE, REG_TYPE, CONST_TYPE, TAG>, KEY_TYPE> {
bool is_constant;
virtual bool ConstantFitsIn32Reg() const { return true; }
const REG_TYPE& reg() const {
XEASSERT(!is_constant);
assert_true(!is_constant);
return reg_;
}
operator const REG_TYPE&() const {
@ -183,33 +183,37 @@ protected:
template <int TAG = -1>
struct I8 : ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> {
typedef ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> BASE;
const int8_t constant() const {
XEASSERT(is_constant);
return value->constant.i8;
assert_true(BASE::is_constant);
return BASE::value->constant.i8;
}
};
template <int TAG = -1>
struct I16 : ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> {
typedef ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> BASE;
const int16_t constant() const {
XEASSERT(is_constant);
return value->constant.i16;
assert_true(BASE::is_constant);
return BASE::value->constant.i16;
}
};
template <int TAG = -1>
struct I32 : ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> {
typedef ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> BASE;
const int32_t constant() const {
XEASSERT(is_constant);
return value->constant.i32;
assert_true(BASE::is_constant);
return BASE::value->constant.i32;
}
};
template <int TAG = -1>
struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
typedef ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> BASE;
const int64_t constant() const {
XEASSERT(is_constant);
return value->constant.i64;
assert_true(BASE::is_constant);
return BASE::value->constant.i64;
}
bool ConstantFitsIn32Reg() const override {
int64_t v = value->constant.i64;
int64_t v = BASE::value->constant.i64;
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
return true;
@ -222,23 +226,26 @@ struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
};
template <int TAG = -1>
struct F32 : ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> {
typedef ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> BASE;
const float constant() const {
XEASSERT(is_constant);
return value->constant.f32;
assert_true(BASE::is_constant);
return BASE::value->constant.f32;
}
};
template <int TAG = -1>
struct F64 : ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> {
typedef ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> BASE;
const double constant() const {
XEASSERT(is_constant);
return value->constant.f64;
assert_true(BASE::is_constant);
return BASE::value->constant.f64;
}
};
template <int TAG = -1>
struct V128 : ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> {
typedef ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> BASE;
const vec128_t& constant() const {
XEASSERT(is_constant);
return value->constant.v128;
assert_true(BASE::is_constant);
return BASE::value->constant.v128;
}
};
@ -308,6 +315,7 @@ template <hir::Opcode OPCODE, typename... Ts>
struct I;
template <hir::Opcode OPCODE, typename DEST>
struct I<OPCODE, DEST> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type>::value;
static const KeyType dest_type = DEST::key_type;
@ -316,7 +324,7 @@ protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table)) {
BASE::LoadDest(i, tag_table)) {
instr = i;
return true;
}
@ -325,6 +333,7 @@ protected:
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
static const KeyType dest_type = DEST::key_type;
@ -335,7 +344,7 @@ protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
BASE::LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1)) {
instr = i;
src1.Load(i->src1);
@ -346,6 +355,7 @@ protected:
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type>::value;
static const KeyType dest_type = DEST::key_type;
@ -358,7 +368,7 @@ protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
BASE::LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1) &&
tag_table.CheckTag<SRC2>(i->src2)) {
instr = i;
@ -371,6 +381,7 @@ protected:
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2, typename SRC3>
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type, SRC3::key_type>::value;
static const KeyType dest_type = DEST::key_type;
@ -385,7 +396,7 @@ protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
BASE::LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1) &&
tag_table.CheckTag<SRC2>(i->src2) &&
tag_table.CheckTag<SRC3>(i->src3)) {
@ -404,7 +415,6 @@ struct SequenceFields;
template <typename I1>
struct SequenceFields<I1> {
I1 i1;
typedef typename I1 I1Type;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
@ -516,9 +526,15 @@ const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
template <typename SEQ, typename T>
struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
typedef Sequence<SingleSequence<SEQ, T>, T> BASE;
typedef T EmitArgType;
static const uint32_t head_key = T::key;
static void Emit(X64Emitter& e, const EmitArgs& _) {
// TODO(benvanik): find a way to do this cross-compiler.
#if XE_COMPILER_MSVC
static uint32_t head_key() { return T::key; }
#else
static constexpr uint32_t head_key() { return T::key; }
#endif // XE_COMPILER_MSVC
static void Emit(X64Emitter& e, const typename BASE::EmitArgs& _) {
SEQ::Emit(e, _.i1);
}
@ -542,12 +558,12 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
if (i.dest == i.src2) {
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
}
@ -560,7 +576,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
@ -584,9 +600,9 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
if (i.dest == i.src2) {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
@ -599,7 +615,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
@ -608,7 +624,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
@ -617,7 +633,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.dest == i.src1) {
reg_reg_fn(e, i.dest, i.src2);
} else if (i.dest == i.src2) {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, temp);
@ -632,7 +648,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
static void EmitCommutativeBinaryXmmOp(
X64Emitter& e, const EmitArgType& i, const FN& fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
e.LoadConstantXmm(e.xmm0, i.src1.constant());
fn(e, i.dest, e.xmm0, i.src2);
} else if (i.src2.is_constant) {
@ -647,7 +663,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
static void EmitAssociativeBinaryXmmOp(
X64Emitter& e, const EmitArgType& i, const FN& fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
e.LoadConstantXmm(e.xmm0, i.src1.constant());
fn(e, i.dest, e.xmm0, i.src2);
} else if (i.src2.is_constant) {
@ -663,11 +679,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.src2, temp);
}
@ -675,7 +691,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.src1, temp);
}
@ -688,11 +704,11 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
assert_true(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()), true);
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2, temp, true);
}
@ -700,7 +716,7 @@ struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()), false);
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, i.src1, temp, false);
}
@ -721,11 +737,9 @@ static const tag_t TAG5 = 5;
static const tag_t TAG6 = 6;
static const tag_t TAG7 = 7;
typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*, const Instr**);
template <typename T>
void Register() {
sequence_table.insert({ T::head_key, T::Select });
sequence_table.insert({ T::head_key(), T::Select });
}
template <typename T, typename Tn, typename... Ts>
void Register() {

File diff suppressed because it is too large Load Diff

View File

@ -10,9 +10,11 @@
#ifndef ALLOY_BACKEND_X64_X64_SEQUENCES_H_
#define ALLOY_BACKEND_X64_X64_SEQUENCES_H_
#include <alloy/core.h>
XEDECLARECLASS2(alloy, hir, Instr);
namespace alloy {
namespace hir {
class Instr;
} // namespace hir
} // namespace alloy
namespace alloy {
namespace backend {
@ -20,14 +22,12 @@ namespace x64 {
class X64Emitter;
void RegisterSequences();
bool SelectSequence(X64Emitter& e, const hir::Instr* i, const hir::Instr** new_tail);
bool SelectSequence(X64Emitter& e, const hir::Instr* i,
const hir::Instr** new_tail);
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_SEQUENCES_H_

View File

@ -7,25 +7,20 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_thunk_emitter.h>
#include "alloy/backend/x64/x64_thunk_emitter.h"
#include <third_party/xbyak/xbyak/xbyak.h>
#include "third_party/xbyak/xbyak/xbyak.h"
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
namespace alloy {
namespace backend {
namespace x64 {
using namespace Xbyak;
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)
: X64Emitter(backend, allocator) {}
X64ThunkEmitter::X64ThunkEmitter(
X64Backend* backend, XbyakAllocator* allocator) :
X64Emitter(backend, allocator) {
}
X64ThunkEmitter::~X64ThunkEmitter() {
}
X64ThunkEmitter::~X64ThunkEmitter() {}
HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
// rcx = target
@ -123,6 +118,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
mov(rax, rdx);
mov(rdx, r8);
mov(r8, r9);
mov(r9, r10);
call(rax);
mov(rbx, qword[rsp + 48]);
@ -143,3 +139,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
void* fn = Emplace(stack_size);
return (HostToGuestThunk)fn;
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -7,19 +7,16 @@
******************************************************************************
*/
#ifndef XENIA_CPU_X64_X64_THUNK_EMITTER_H_
#define XENIA_CPU_X64_X64_THUNK_EMITTER_H_
#include <alloy/core.h>
#include <alloy/backend/x64/x64_backend.h>
#include <alloy/backend/x64/x64_emitter.h>
#ifndef ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
#define ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_
#include "alloy/backend/x64/x64_backend.h"
#include "alloy/backend/x64/x64_emitter.h"
namespace alloy {
namespace backend {
namespace x64 {
/**
* Stack Layout
* ----------------------------
@ -99,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 ...
* +------------------+
@ -119,13 +116,12 @@ class StackLayout {
public:
const static size_t THUNK_STACK_SIZE = 120;
const static size_t GUEST_STACK_SIZE = 88;
const static size_t GUEST_RCX_HOME = 64;
const static size_t GUEST_RET_ADDR = 72;
const static size_t GUEST_CALL_RET_ADDR = 80;
const static size_t GUEST_STACK_SIZE = 104;
const static size_t GUEST_RCX_HOME = 80;
const static size_t GUEST_RET_ADDR = 88;
const static size_t GUEST_CALL_RET_ADDR = 96;
};
class X64ThunkEmitter : public X64Emitter {
public:
X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator);
@ -138,10 +134,8 @@ public:
GuestToHostThunk EmitGuestToHostThunk();
};
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // XENIA_CPU_X64_X64_THUNK_EMITTER_H_
#endif // ALLOY_BACKEND_X64_X64_THUNK_EMITTER_H_

View File

@ -7,11 +7,11 @@
******************************************************************************
*/
#include <alloy/backend/x64/x64_tracers.h>
#include "alloy/backend/x64/x64_tracers.h"
#include <alloy/backend/x64/x64_emitter.h>
#include <alloy/runtime/runtime.h>
#include <alloy/runtime/thread_state.h>
#include "alloy/backend/x64/x64_emitter.h"
#include "alloy/runtime/runtime.h"
#include "alloy/runtime/thread_state.h"
using namespace alloy;
using namespace alloy::backend::x64;
@ -27,9 +27,12 @@ namespace x64 {
#define TARGET_THREAD 1
#define IFLUSH() fflush(stdout)
#define IPRINT if (thread_state->thread_id() == TARGET_THREAD) printf
#define IPRINT \
if (thread_state->thread_id() == TARGET_THREAD) printf
#define DFLUSH() fflush(stdout)
#define DPRINT DFLUSH(); if (thread_state->thread_id() == TARGET_THREAD) printf
#define DPRINT \
DFLUSH(); \
if (thread_state->thread_id() == TARGET_THREAD) printf
uint32_t GetTracingMode() {
uint32_t mode = 0;
@ -50,151 +53,148 @@ void TraceString(void* raw_context, const char* str) {
void TraceContextLoadI8(void* raw_context, uint64_t offset, uint8_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = ctx i8 +%d\n", (int8_t)value, value, offset);
DPRINT("%d (%X) = ctx i8 +%llu\n", (int8_t)value, value, offset);
}
void TraceContextLoadI16(void* raw_context, uint64_t offset, uint16_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = ctx i16 +%d\n", (int16_t)value, value, offset);
DPRINT("%d (%X) = ctx i16 +%llu\n", (int16_t)value, value, offset);
}
void TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = ctx i32 +%d\n", (int32_t)value, value, offset);
DPRINT("%d (%X) = ctx i32 +%llu\n", (int32_t)value, value, offset);
}
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%lld (%llX) = ctx i64 +%d\n", (int64_t)value, value, offset);
DPRINT("%lld (%llX) = ctx i64 +%llu\n", (int64_t)value, value, offset);
}
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%e (%X) = ctx f32 +%d\n", value.m128_f32[0], value.m128_i32[0], offset);
DPRINT("%e (%X) = ctx f32 +%llu\n", poly::m128_f32<0>(value),
poly::m128_i32<0>(value), offset);
}
void TraceContextLoadF64(void* raw_context, uint64_t offset, __m128 value) {
void TraceContextLoadF64(void* raw_context, uint64_t offset,
const double* value) {
auto thread_state = *((ThreadState**)raw_context);
union {
double d;
uint64_t x;
} f;
f.x = value.m128_i64[0];
DPRINT("%lle (%llX) = ctx f64 +%d\n", f.d, value.m128_i64[0], offset);
auto v = _mm_loadu_pd(value);
DPRINT("%le (%llX) = ctx f64 +%llu\n", poly::m128_f64<0>(v),
poly::m128_i64<0>(v), offset);
}
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%d\n",
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
offset);
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = ctx v128 +%llu\n",
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
poly::m128_i32<2>(value), poly::m128_i32<3>(value), offset);
}
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx i8 +%d = %d (%X)\n", offset, (int8_t)value, value);
DPRINT("ctx i8 +%llu = %d (%X)\n", offset, (int8_t)value, value);
}
void TraceContextStoreI16(void* raw_context, uint64_t offset, uint16_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx i16 +%d = %d (%X)\n", offset, (int16_t)value, value);
DPRINT("ctx i16 +%llu = %d (%X)\n", offset, (int16_t)value, value);
}
void TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx i32 +%d = %d (%X)\n", offset, (int32_t)value, value);
DPRINT("ctx i32 +%llu = %d (%X)\n", offset, (int32_t)value, value);
}
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx i64 +%d = %lld (%llX)\n", offset, (int64_t)value, value);
DPRINT("ctx i64 +%llu = %lld (%llX)\n", offset, (int64_t)value, value);
}
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx f32 +%d = %e (%X)\n", offset, value.m128_i32[0], value.m128_f32[0]);
DPRINT("ctx f32 +%llu = %e (%X)\n", offset, poly::m128_f32<0>(value),
poly::m128_i32<0>(value));
}
void TraceContextStoreF64(void* raw_context, uint64_t offset, __m128 value) {
void TraceContextStoreF64(void* raw_context, uint64_t offset,
const double* value) {
auto thread_state = *((ThreadState**)raw_context);
union {
double d;
uint64_t x;
} f;
f.x = value.m128_i64[0];
DPRINT("ctx f64 +%d = %lle (%llX)\n", offset, value.m128_i64[0], f.d);
auto v = _mm_loadu_pd(value);
DPRINT("ctx f64 +%llu = %le (%llX)\n", offset, poly::m128_f64<0>(v),
poly::m128_i64<0>(v));
}
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("ctx v128 +%d = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", offset,
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
DPRINT("ctx v128 +%llu = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", offset,
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
poly::m128_i32<2>(value), poly::m128_i32<3>(value));
}
void TraceMemoryLoadI8(void* raw_context, uint64_t address, uint8_t value) {
void TraceMemoryLoadI8(void* raw_context, uint32_t address, uint8_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = load.i8 %.8X\n", (int8_t)value, value, address);
}
void TraceMemoryLoadI16(void* raw_context, uint64_t address, uint16_t value) {
void TraceMemoryLoadI16(void* raw_context, uint32_t address, uint16_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = load.i16 %.8X\n", (int16_t)value, value, address);
}
void TraceMemoryLoadI32(void* raw_context, uint64_t address, uint32_t value) {
void TraceMemoryLoadI32(void* raw_context, uint32_t address, uint32_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%d (%X) = load.i32 %.8X\n", (int32_t)value, value, address);
}
void TraceMemoryLoadI64(void* raw_context, uint64_t address, uint64_t value) {
void TraceMemoryLoadI64(void* raw_context, uint32_t address, uint64_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%lld (%llX) = load.i64 %.8X\n", (int64_t)value, value, address);
}
void TraceMemoryLoadF32(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryLoadF32(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("%e (%X) = load.f32 %.8X\n", value.m128_f32[0], value.m128_i32[0], address);
DPRINT("%e (%X) = load.f32 %.8X\n", poly::m128_f32<0>(value),
poly::m128_i32<0>(value), address);
}
void TraceMemoryLoadF64(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryLoadF64(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
union {
double d;
uint64_t x;
} f;
f.x = value.m128_i64[0];
DPRINT("%lle (%llX) = load.f64 %.8X\n", f.d, value.m128_i64[0], address);
DPRINT("%le (%llX) = load.f64 %.8X\n", poly::m128_f64<0>(value),
poly::m128_i64<0>(value), address);
}
void TraceMemoryLoadV128(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryLoadV128(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("[%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X] = load.v128 %.8X\n",
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3],
address);
poly::m128_f32<0>(value), poly::m128_f32<1>(value),
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
poly::m128_i32<2>(value), poly::m128_i32<3>(value), address);
}
void TraceMemoryStoreI8(void* raw_context, uint64_t address, uint8_t value) {
void TraceMemoryStoreI8(void* raw_context, uint32_t address, uint8_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.i8 %.8X = %d (%X)\n", address, (int8_t)value, value);
}
void TraceMemoryStoreI16(void* raw_context, uint64_t address, uint16_t value) {
void TraceMemoryStoreI16(void* raw_context, uint32_t address, uint16_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.i16 %.8X = %d (%X)\n", address, (int16_t)value, value);
}
void TraceMemoryStoreI32(void* raw_context, uint64_t address, uint32_t value) {
void TraceMemoryStoreI32(void* raw_context, uint32_t address, uint32_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.i32 %.8X = %d (%X)\n", address, (int32_t)value, value);
}
void TraceMemoryStoreI64(void* raw_context, uint64_t address, uint64_t value) {
void TraceMemoryStoreI64(void* raw_context, uint32_t address, uint64_t value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.i64 %.8X = %lld (%llX)\n", address, (int64_t)value, value);
}
void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryStoreF32(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.f32 %.8X = %e (%X)\n", address, value.m128_f32[0], value.m128_i32[0]);
DPRINT("store.f32 %.8X = %e (%X)\n", address, poly::m128_f32<0>(value),
poly::m128_i32<0>(value));
}
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryStoreF64(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
union {
double d;
uint64_t x;
} f;
f.x = value.m128_i64[0];
DPRINT("store.f64 %.8X = %lle (%llX)\n", address, f.d, value.m128_i64[0]);
DPRINT("store.f64 %.8X = %le (%llX)\n", address, poly::m128_f64<0>(value),
poly::m128_i64<0>(value));
}
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value) {
void TraceMemoryStoreV128(void* raw_context, uint32_t address, __m128 value) {
auto thread_state = *((ThreadState**)raw_context);
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n", address,
value.m128_f32[0], value.m128_f32[1], value.m128_f32[2], value.m128_f32[3],
value.m128_i32[0], value.m128_i32[1], value.m128_i32[2], value.m128_i32[3]);
DPRINT("store.v128 %.8X = [%e, %e, %e, %e] [%.8X, %.8X, %.8X, %.8X]\n",
address, poly::m128_f32<0>(value), poly::m128_f32<1>(value),
poly::m128_f32<2>(value), poly::m128_f32<3>(value),
poly::m128_i32<0>(value), poly::m128_i32<1>(value),
poly::m128_i32<2>(value), poly::m128_i32<3>(value));
}
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@ -10,24 +10,8 @@
#ifndef ALLOY_BACKEND_X64_X64_TRACERS_H_
#define ALLOY_BACKEND_X64_X64_TRACERS_H_
#include <alloy/core.h>
#if XE_LIKE_WIN32
#include <xmmintrin.h>
#else
typedef union __declspec(align(16)) __m128 {
float m128_f32[4];
uint64_t m128_u64[2];
int8_t m128_i8[16];
int16_t m128_i16[8];
int32_t m128_i32[4];
int64_t m128_i64[2];
uint8_t m128_u8[16];
uint16_t m128_u16[8];
uint32_t m128_u32[4];
} __m128;
#endif
#include <cstdint>
namespace alloy {
namespace backend {
@ -50,7 +34,8 @@ void TraceContextLoadI16(void* raw_context, uint64_t offset, uint16_t value);
void TraceContextLoadI32(void* raw_context, uint64_t offset, uint32_t value);
void TraceContextLoadI64(void* raw_context, uint64_t offset, uint64_t value);
void TraceContextLoadF32(void* raw_context, uint64_t offset, __m128 value);
void TraceContextLoadF64(void* raw_context, uint64_t offset, __m128 value);
void TraceContextLoadF64(void* raw_context, uint64_t offset,
const double* value);
void TraceContextLoadV128(void* raw_context, uint64_t offset, __m128 value);
void TraceContextStoreI8(void* raw_context, uint64_t offset, uint8_t value);
@ -58,28 +43,28 @@ void TraceContextStoreI16(void* raw_context, uint64_t offset, uint16_t value);
void TraceContextStoreI32(void* raw_context, uint64_t offset, uint32_t value);
void TraceContextStoreI64(void* raw_context, uint64_t offset, uint64_t value);
void TraceContextStoreF32(void* raw_context, uint64_t offset, __m128 value);
void TraceContextStoreF64(void* raw_context, uint64_t offset, __m128 value);
void TraceContextStoreF64(void* raw_context, uint64_t offset,
const double* value);
void TraceContextStoreV128(void* raw_context, uint64_t offset, __m128 value);
void TraceMemoryLoadI8(void* raw_context, uint64_t address, uint8_t value);
void TraceMemoryLoadI16(void* raw_context, uint64_t address, uint16_t value);
void TraceMemoryLoadI32(void* raw_context, uint64_t address, uint32_t value);
void TraceMemoryLoadI64(void* raw_context, uint64_t address, uint64_t value);
void TraceMemoryLoadF32(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryLoadF64(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryLoadV128(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryLoadI8(void* raw_context, uint32_t address, uint8_t value);
void TraceMemoryLoadI16(void* raw_context, uint32_t address, uint16_t value);
void TraceMemoryLoadI32(void* raw_context, uint32_t address, uint32_t value);
void TraceMemoryLoadI64(void* raw_context, uint32_t address, uint64_t value);
void TraceMemoryLoadF32(void* raw_context, uint32_t address, __m128 value);
void TraceMemoryLoadF64(void* raw_context, uint32_t address, __m128 value);
void TraceMemoryLoadV128(void* raw_context, uint32_t address, __m128 value);
void TraceMemoryStoreI8(void* raw_context, uint64_t address, uint8_t value);
void TraceMemoryStoreI16(void* raw_context, uint64_t address, uint16_t value);
void TraceMemoryStoreI32(void* raw_context, uint64_t address, uint32_t value);
void TraceMemoryStoreI64(void* raw_context, uint64_t address, uint64_t value);
void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryStoreI8(void* raw_context, uint32_t address, uint8_t value);
void TraceMemoryStoreI16(void* raw_context, uint32_t address, uint16_t value);
void TraceMemoryStoreI32(void* raw_context, uint32_t address, uint32_t value);
void TraceMemoryStoreI64(void* raw_context, uint32_t address, uint64_t value);
void TraceMemoryStoreF32(void* raw_context, uint32_t address, __m128 value);
void TraceMemoryStoreF64(void* raw_context, uint32_t address, __m128 value);
void TraceMemoryStoreV128(void* raw_context, uint32_t address, __m128 value);
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_TRACERS_H_

View File

@ -7,55 +7,36 @@
******************************************************************************
*/
#include <alloy/compiler/compiler.h>
#include "alloy/compiler/compiler.h"
#include <alloy/compiler/compiler_pass.h>
#include <alloy/compiler/tracing.h>
#include "alloy/compiler/compiler_pass.h"
#include "xenia/profiling.h"
using namespace alloy;
using namespace alloy::compiler;
using namespace alloy::hir;
using namespace alloy::runtime;
namespace alloy {
namespace compiler {
using alloy::hir::HIRBuilder;
using alloy::runtime::Runtime;
Compiler::Compiler(Runtime* runtime) :
runtime_(runtime) {
scratch_arena_ = new Arena();
Compiler::Compiler(Runtime* runtime) : runtime_(runtime) {}
alloy::tracing::WriteEvent(EventType::Init({
}));
}
Compiler::~Compiler() { Reset(); }
Compiler::~Compiler() {
Reset();
for (auto it = passes_.begin(); it != passes_.end(); ++it) {
CompilerPass* pass = *it;
delete pass;
}
delete scratch_arena_;
alloy::tracing::WriteEvent(EventType::Deinit({
}));
}
void Compiler::AddPass(CompilerPass* pass) {
void Compiler::AddPass(std::unique_ptr<CompilerPass> pass) {
pass->Initialize(this);
passes_.push_back(pass);
passes_.push_back(std::move(pass));
}
void Compiler::Reset() {
}
void Compiler::Reset() {}
int Compiler::Compile(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
// TODO(benvanik): sophisticated stuff. Run passes in parallel, run until they
// stop changing things, etc.
for (auto it = passes_.begin(); it != passes_.end(); ++it) {
CompilerPass* pass = *it;
scratch_arena_->Reset();
for (size_t i = 0; i < passes_.size(); ++i) {
auto& pass = passes_[i];
scratch_arena_.Reset();
if (pass->Run(builder)) {
return 1;
}
@ -63,3 +44,6 @@ int Compiler::Compile(HIRBuilder* builder) {
return 0;
}
} // namespace compiler
} // namespace alloy

View File

@ -10,27 +10,31 @@
#ifndef ALLOY_COMPILER_COMPILER_H_
#define ALLOY_COMPILER_COMPILER_H_
#include <alloy/core.h>
#include <alloy/hir/hir_builder.h>
#include <memory>
#include <vector>
namespace alloy { namespace runtime { class Runtime; } }
#include "alloy/hir/hir_builder.h"
namespace alloy {
namespace runtime {
class Runtime;
} // namespace runtime
} // namespace alloy
namespace alloy {
namespace compiler {
class CompilerPass;
class Compiler {
public:
Compiler(runtime::Runtime* runtime);
~Compiler();
runtime::Runtime* runtime() const { return runtime_; }
Arena* scratch_arena() const { return scratch_arena_; }
Arena* scratch_arena() { return &scratch_arena_; }
void AddPass(CompilerPass* pass);
void AddPass(std::unique_ptr<CompilerPass> pass);
void Reset();
@ -38,15 +42,12 @@ public:
private:
runtime::Runtime* runtime_;
Arena* scratch_arena_;
Arena scratch_arena_;
typedef std::vector<CompilerPass*> PassList;
PassList passes_;
std::vector<std::unique_ptr<CompilerPass>> passes_;
};
} // namespace compiler
} // namespace alloy
#endif // ALLOY_COMPILER_COMPILER_H_

View File

@ -7,20 +7,16 @@
******************************************************************************
*/
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
#include <alloy/compiler/compiler.h>
#include "alloy/compiler/compiler.h"
using namespace alloy;
using namespace alloy::compiler;
namespace alloy {
namespace compiler {
CompilerPass::CompilerPass() : runtime_(0), compiler_(0) {}
CompilerPass::CompilerPass() :
runtime_(0), compiler_(0) {
}
CompilerPass::~CompilerPass() {
}
CompilerPass::~CompilerPass() = default;
int CompilerPass::Initialize(Compiler* compiler) {
runtime_ = compiler->runtime();
@ -31,3 +27,6 @@ int CompilerPass::Initialize(Compiler* compiler) {
Arena* CompilerPass::scratch_arena() const {
return compiler_->scratch_arena();
}
} // namespace compiler
} // namespace alloy

View File

@ -10,19 +10,19 @@
#ifndef ALLOY_COMPILER_COMPILER_PASS_H_
#define ALLOY_COMPILER_COMPILER_PASS_H_
#include <alloy/core.h>
#include <alloy/hir/hir_builder.h>
namespace alloy { namespace runtime { class Runtime; } }
#include "alloy/hir/hir_builder.h"
namespace alloy {
namespace runtime {
class Runtime;
} // namespace runtime
} // namespace alloy
namespace alloy {
namespace compiler {
class Compiler;
class CompilerPass {
public:
CompilerPass();
@ -40,9 +40,7 @@ protected:
Compiler* compiler_;
};
} // namespace compiler
} // namespace alloy
#endif // ALLOY_COMPILER_COMPILER_PASS_H_

View File

@ -10,17 +10,18 @@
#ifndef ALLOY_COMPILER_COMPILER_PASSES_H_
#define ALLOY_COMPILER_COMPILER_PASSES_H_
#include <alloy/compiler/passes/constant_propagation_pass.h>
#include <alloy/compiler/passes/control_flow_analysis_pass.h>
#include <alloy/compiler/passes/context_promotion_pass.h>
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
//#include <alloy/compiler/passes/dead_store_elimination_pass.h>
#include <alloy/compiler/passes/finalization_pass.h>
#include <alloy/compiler/passes/register_allocation_pass.h>
#include <alloy/compiler/passes/simplification_pass.h>
#include <alloy/compiler/passes/validation_pass.h>
#include <alloy/compiler/passes/value_reduction_pass.h>
#include "alloy/compiler/passes/constant_propagation_pass.h"
#include "alloy/compiler/passes/context_promotion_pass.h"
#include "alloy/compiler/passes/control_flow_analysis_pass.h"
#include "alloy/compiler/passes/control_flow_simplification_pass.h"
#include "alloy/compiler/passes/data_flow_analysis_pass.h"
#include "alloy/compiler/passes/dead_code_elimination_pass.h"
//#include "alloy/compiler/passes/dead_store_elimination_pass.h"
#include "alloy/compiler/passes/finalization_pass.h"
#include "alloy/compiler/passes/register_allocation_pass.h"
#include "alloy/compiler/passes/simplification_pass.h"
#include "alloy/compiler/passes/validation_pass.h"
#include "alloy/compiler/passes/value_reduction_pass.h"
// TODO:
// - mark_use/mark_set
@ -49,7 +50,6 @@
// Block gets:
// AddIncomingValue(Value* value, Block* src_block) ??
// Potentially interesting passes:
//
// Run order:

View File

@ -7,23 +7,26 @@
******************************************************************************
*/
#include <alloy/compiler/passes/constant_propagation_pass.h>
#include "alloy/compiler/passes/constant_propagation_pass.h"
#include <alloy/runtime/function.h>
#include <alloy/runtime/runtime.h>
#include "alloy/runtime/function.h"
#include "alloy/runtime/runtime.h"
#include "xenia/profiling.h"
using namespace alloy;
using namespace alloy::compiler;
using namespace alloy::compiler::passes;
namespace alloy {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using alloy::hir::HIRBuilder;
using alloy::hir::TypeName;
using alloy::hir::Value;
ConstantPropagationPass::ConstantPropagationPass() :
CompilerPass() {
}
ConstantPropagationPass::ConstantPropagationPass() : CompilerPass() {}
ConstantPropagationPass::~ConstantPropagationPass() {
}
ConstantPropagationPass::~ConstantPropagationPass() {}
int ConstantPropagationPass::Run(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
@ -55,11 +58,11 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
// v1 = 19
// v2 = 0
Block* block = builder->first_block();
auto block = builder->first_block();
while (block) {
Instr* i = block->instr_head;
auto i = block->instr_head;
while (i) {
Value* v = i->dest;
auto v = i->dest;
switch (i->opcode->num) {
case OPCODE_DEBUG_BREAK_TRUE:
if (i->src1.value->IsConstant()) {
@ -275,13 +278,13 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
break;
case OPCODE_DID_CARRY:
XEASSERT(!i->src1.value->IsConstant());
assert_true(!i->src1.value->IsConstant());
break;
case OPCODE_DID_OVERFLOW:
XEASSERT(!i->src1.value->IsConstant());
assert_true(!i->src1.value->IsConstant());
break;
case OPCODE_DID_SATURATE:
XEASSERT(!i->src1.value->IsConstant());
assert_true(!i->src1.value->IsConstant());
break;
case OPCODE_ADD:
@ -298,6 +301,31 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
}
break;
// TODO(benvanik): ADD_CARRY (w/ ARITHMETIC_SET_CARRY)
case OPCODE_ADD_CARRY:
if (i->src1.value->IsConstantZero() && i->src2.value->IsConstantZero()) {
Value* ca = i->src3.value;
// If carry is set find the DID_CARRY and fix it.
if (!!(i->flags & ARITHMETIC_SET_CARRY)) {
auto next = i->dest->use_head;
while (next) {
auto use = next;
next = use->next;
if (use->instr->opcode == &OPCODE_DID_CARRY_info) {
// Replace carry value.
use->instr->Replace(&OPCODE_ASSIGN_info, 0);
use->instr->set_src1(ca);
}
}
}
if (i->dest->type == ca->type) {
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(ca);
} else {
i->Replace(&OPCODE_ZERO_EXTEND_info, 0);
i->set_src1(ca);
}
}
break;
case OPCODE_SUB:
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
v->set_from(i->src1.value);
@ -428,6 +456,10 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
// Quite a few of these, from building vec128s.
}
break;
default:
// Ignored.
break;
}
i = i->next;
}
@ -438,7 +470,7 @@ int ConstantPropagationPass::Run(HIRBuilder* builder) {
return 0;
}
void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
void ConstantPropagationPass::PropagateCarry(Value* v, bool did_carry) {
auto next = v->use_head;
while (next) {
auto use = next;
@ -450,3 +482,7 @@ void ConstantPropagationPass::PropagateCarry(hir::Value* v, bool did_carry) {
}
}
}
} // namespace passes
} // namespace compiler
} // namespace alloy

View File

@ -10,29 +10,25 @@
#ifndef ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
#define ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
namespace alloy {
namespace compiler {
namespace passes {
class ConstantPropagationPass : public CompilerPass {
public:
ConstantPropagationPass();
virtual ~ConstantPropagationPass();
~ConstantPropagationPass() override;
virtual int Run(hir::HIRBuilder* builder);
int Run(hir::HIRBuilder* builder) override;
private:
void PropagateCarry(hir::Value* v, bool did_carry);
};
} // namespace passes
} // namespace compiler
} // namespace alloy
#endif // ALLOY_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_

View File

@ -7,35 +7,33 @@
******************************************************************************
*/
#include <alloy/compiler/passes/context_promotion_pass.h>
#include "alloy/compiler/passes/context_promotion_pass.h"
#include <gflags/gflags.h>
#include <alloy/compiler/compiler.h>
#include <alloy/runtime/runtime.h>
using namespace alloy;
using namespace alloy::compiler;
using namespace alloy::compiler::passes;
using namespace alloy::frontend;
using namespace alloy::hir;
using namespace alloy::runtime;
#include "alloy/compiler/compiler.h"
#include "alloy/runtime/runtime.h"
#include "xenia/profiling.h"
DEFINE_bool(store_all_context_values, false,
"Don't strip dead context stores to aid in debugging.");
namespace alloy {
namespace compiler {
namespace passes {
ContextPromotionPass::ContextPromotionPass() :
context_values_size_(0), context_values_(0),
CompilerPass() {
}
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
ContextPromotionPass::~ContextPromotionPass() {
if (context_values_) {
xe_free(context_values_);
}
}
using alloy::frontend::ContextInfo;
using alloy::hir::Block;
using alloy::hir::HIRBuilder;
using alloy::hir::Instr;
using alloy::hir::Value;
ContextPromotionPass::ContextPromotionPass() : CompilerPass() {}
ContextPromotionPass::~ContextPromotionPass() {}
int ContextPromotionPass::Initialize(Compiler* compiler) {
if (CompilerPass::Initialize(compiler)) {
@ -44,8 +42,8 @@ int ContextPromotionPass::Initialize(Compiler* compiler) {
// This is a terrible implementation.
ContextInfo* context_info = runtime_->frontend()->context_info();
context_values_size_ = context_info->size() * sizeof(Value*);
context_values_ = (Value**)xe_calloc(context_values_size_);
context_values_.resize(context_info->size());
context_validity_.resize(static_cast<uint32_t>(context_info->size()));
return 0;
}
@ -70,7 +68,7 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
// Promote loads to values.
// Process each block independently, for now.
Block* block = builder->first_block();
auto block = builder->first_block();
while (block) {
PromoteBlock(block);
block = block->next;
@ -89,50 +87,55 @@ int ContextPromotionPass::Run(HIRBuilder* builder) {
}
void ContextPromotionPass::PromoteBlock(Block* block) {
// Clear the context values list.
// TODO(benvanik): new data structure that isn't so stupid.
// Bitvector of validity, perhaps?
xe_zero_struct(context_values_, context_values_size_);
auto& validity = context_validity_;
validity.reset();
Instr* i = block->instr_head;
while (i) {
if (i->opcode == &OPCODE_LOAD_CONTEXT_info) {
auto next = i->next;
if (i->opcode->flags & OPCODE_FLAG_VOLATILE) {
// Volatile instruction - requires all context values be flushed.
validity.reset();
} else if (i->opcode == &OPCODE_LOAD_CONTEXT_info) {
size_t offset = i->src1.offset;
Value* previous_value = context_values_[offset];
if (previous_value) {
if (validity.test(static_cast<uint32_t>(offset))) {
// Legit previous value, reuse.
Value* previous_value = context_values_[offset];
i->opcode = &hir::OPCODE_ASSIGN_info;
i->set_src1(previous_value);
} else {
// Store the loaded value into the table.
context_values_[offset] = i->dest;
validity.set(static_cast<uint32_t>(offset));
}
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
size_t offset = i->src1.offset;
Value* value = i->src2.value;
// Store value into the table for later.
context_values_[offset] = value;
validity.set(static_cast<uint32_t>(offset));
}
i = i->next;
i = next;
}
}
void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
// TODO(benvanik): use a bitvector.
// To avoid clearing the structure, we use a token.
Value* token = (Value*)block;
auto& validity = context_validity_;
validity.reset();
// Walk backwards and mark offsets that are written to.
// If the offset was written to earlier, ignore the store.
Instr* i = block->instr_tail;
while (i) {
Instr* prev = i->prev;
if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
if (i->opcode->flags & (OPCODE_FLAG_VOLATILE | OPCODE_FLAG_BRANCH)) {
// Volatile instruction - requires all context values be flushed.
validity.reset();
} else if (i->opcode == &OPCODE_STORE_CONTEXT_info) {
size_t offset = i->src1.offset;
if (context_values_[offset] != token) {
// Mark offset as written to.
context_values_[offset] = token;
if (!validity.test(static_cast<uint32_t>(offset))) {
// Offset not yet written, mark and continue.
validity.set(static_cast<uint32_t>(offset));
} else {
// Already written to. Remove this store.
i->Remove();
@ -141,3 +144,7 @@ void ContextPromotionPass::RemoveDeadStoresBlock(Block* block) {
i = prev;
}
}
} // namespace passes
} // namespace compiler
} // namespace alloy

View File

@ -10,36 +10,43 @@
#ifndef ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
#define ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
#if XE_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4267)
#include <llvm/ADT/BitVector.h>
#pragma warning(pop)
#else
#include <cmath>
#include <llvm/ADT/BitVector.h>
#endif // XE_COMPILER_MSVC
namespace alloy {
namespace compiler {
namespace passes {
class ContextPromotionPass : public CompilerPass {
public:
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:
void PromoteBlock(hir::Block* block);
void RemoveDeadStoresBlock(hir::Block* block);
private:
size_t context_values_size_;
hir::Value** context_values_;
std::vector<hir::Value*> context_values_;
llvm::BitVector context_validity_;
};
} // namespace passes
} // namespace compiler
} // namespace alloy
#endif // ALLOY_COMPILER_PASSES_CONTEXT_PROMOTION_PASS_H_

View File

@ -7,35 +7,42 @@
******************************************************************************
*/
#include <alloy/compiler/passes/control_flow_analysis_pass.h>
#include "alloy/compiler/passes/control_flow_analysis_pass.h"
#include <alloy/backend/backend.h>
#include <alloy/compiler/compiler.h>
#include <alloy/runtime/runtime.h>
#include "alloy/backend/backend.h"
#include "alloy/compiler/compiler.h"
#include "alloy/runtime/runtime.h"
#include "xenia/profiling.h"
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::compiler;
using namespace alloy::compiler::passes;
using namespace alloy::frontend;
namespace alloy {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using namespace alloy::runtime;
using alloy::hir::Edge;
using alloy::hir::HIRBuilder;
ControlFlowAnalysisPass::ControlFlowAnalysisPass() :
CompilerPass() {
}
ControlFlowAnalysisPass::ControlFlowAnalysisPass() : CompilerPass() {}
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {
}
ControlFlowAnalysisPass::~ControlFlowAnalysisPass() {}
int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
// TODO(benvanik): reset edges for all blocks? Needed to be re-runnable.
// Reset edges for all blocks. Needed to be re-runnable.
// Note that this wastes a bunch of arena memory, so we shouldn't
// re-run too often.
auto block = builder->first_block();
while (block) {
block->incoming_edge_head = nullptr;
block->outgoing_edge_head = nullptr;
block = block->next;
}
// Add edges.
auto block = builder->first_block();
block = builder->first_block();
while (block) {
auto instr = block->instr_tail;
while (instr) {
@ -67,3 +74,7 @@ int ControlFlowAnalysisPass::Run(HIRBuilder* builder) {
return 0;
}
} // namespace passes
} // namespace compiler
} // namespace alloy

View File

@ -10,28 +10,24 @@
#ifndef ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
#define ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
namespace alloy {
namespace compiler {
namespace passes {
class ControlFlowAnalysisPass : public CompilerPass {
public:
ControlFlowAnalysisPass();
virtual ~ControlFlowAnalysisPass();
~ControlFlowAnalysisPass() override;
virtual int Run(hir::HIRBuilder* builder);
int Run(hir::HIRBuilder* builder) override;
private:
};
} // namespace passes
} // namespace compiler
} // namespace alloy
#endif // ALLOY_COMPILER_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_

View File

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

View File

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

View File

@ -7,33 +7,37 @@
******************************************************************************
*/
#include <alloy/compiler/passes/data_flow_analysis_pass.h>
#include "alloy/compiler/passes/data_flow_analysis_pass.h"
#include <alloy/backend/backend.h>
#include <alloy/compiler/compiler.h>
#include <alloy/runtime/runtime.h>
#include "alloy/backend/backend.h"
#include "alloy/compiler/compiler.h"
#include "alloy/runtime/runtime.h"
#include "xenia/profiling.h"
#if XE_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4267)
#include <llvm/ADT/BitVector.h>
#pragma warning(pop)
#else
#include <llvm/ADT/BitVector.h>
#endif // XE_COMPILER_MSVC
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::compiler;
using namespace alloy::compiler::passes;
using namespace alloy::frontend;
namespace alloy {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using namespace alloy::runtime;
using alloy::hir::HIRBuilder;
using alloy::hir::OpcodeSignatureType;
using alloy::hir::Value;
DataFlowAnalysisPass::DataFlowAnalysisPass() :
CompilerPass() {
}
DataFlowAnalysisPass::DataFlowAnalysisPass() : CompilerPass() {}
DataFlowAnalysisPass::~DataFlowAnalysisPass() {
}
DataFlowAnalysisPass::~DataFlowAnalysisPass() {}
int DataFlowAnalysisPass::Run(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
@ -66,15 +70,15 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
// Stash for value map. We may want to maintain this during building.
auto arena = builder->arena();
Value** value_map = (Value**)arena->Alloc(
sizeof(Value*) * max_value_estimate);
Value** value_map =
(Value**)arena->Alloc(sizeof(Value*) * max_value_estimate);
// Allocate incoming bitvectors for use by blocks. We don't need outgoing
// because they are only used during the block iteration.
// Mapped by block ordinal.
// TODO(benvanik): cache this list, grow as needed, etc.
auto incoming_bitvectors = (llvm::BitVector**)arena->Alloc(
sizeof(llvm::BitVector*) * block_count);
auto incoming_bitvectors =
(llvm::BitVector**)arena->Alloc(sizeof(llvm::BitVector*) * block_count);
for (auto n = 0u; n < block_count; n++) {
incoming_bitvectors[n] = new llvm::BitVector(max_value_estimate);
}
@ -94,7 +98,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
if (v->def && v->def->block != block) { \
incoming_values.set(v->ordinal); \
} \
XEASSERT(v->ordinal < max_value_estimate); \
assert_true(v->ordinal < max_value_estimate); \
value_map[v->ordinal] = v;
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
SET_INCOMING_VALUE(instr->src1.value);
@ -125,7 +129,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
auto outgoing_ordinal = outgoing_values.find_first();
while (outgoing_ordinal != -1) {
Value* src_value = value_map[outgoing_ordinal];
XEASSERTNOTNULL(src_value);
assert_not_null(src_value);
if (!src_value->local_slot) {
src_value->local_slot = builder->AllocLocal(src_value->type);
}
@ -139,7 +143,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
while (def_next && def_next->opcode->flags & OPCODE_FLAG_PAIRED_PREV) {
def_next = def_next->next;
}
XEASSERTNOTNULL(def_next);
assert_not_null(def_next);
builder->last_instr()->MoveBefore(def_next);
// We don't need it in the incoming list.
@ -150,7 +154,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
while (tail && tail->opcode->flags & OPCODE_FLAG_BRANCH) {
tail = tail->prev;
}
XEASSERTNOTZERO(tail);
assert_not_zero(tail);
builder->last_instr()->MoveBefore(tail->next);
}
@ -161,7 +165,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
auto incoming_ordinal = incoming_values.find_first();
while (incoming_ordinal != -1) {
Value* src_value = value_map[incoming_ordinal];
XEASSERTNOTNULL(src_value);
assert_not_null(src_value);
if (!src_value->local_slot) {
src_value->local_slot = builder->AllocLocal(src_value->type);
}
@ -169,7 +173,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
builder->last_instr()->MoveBefore(block->instr_head);
// Swap uses of original value with the local value.
auto instr = block->instr_head;
instr = block->instr_head;
while (instr) {
uint32_t signature = instr->opcode->signature;
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
@ -201,3 +205,7 @@ void DataFlowAnalysisPass::AnalyzeFlow(HIRBuilder* builder,
delete incoming_bitvectors[n];
}
}
} // namespace passes
} // namespace compiler
} // namespace alloy

View File

@ -10,7 +10,7 @@
#ifndef ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
#define ALLOY_COMPILER_PASSES_DATA_FLOW_ANALYSIS_PASS_H_
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
namespace alloy {
@ -21,9 +21,9 @@ namespace passes {
class DataFlowAnalysisPass : public CompilerPass {
public:
DataFlowAnalysisPass();
virtual ~DataFlowAnalysisPass();
~DataFlowAnalysisPass() override;
virtual int Run(hir::HIRBuilder* builder);
int Run(hir::HIRBuilder* builder) override;
private:
uint32_t LinearizeBlocks(hir::HIRBuilder* builder);

View File

@ -7,20 +7,24 @@
******************************************************************************
*/
#include <alloy/compiler/passes/dead_code_elimination_pass.h>
#include "alloy/compiler/passes/dead_code_elimination_pass.h"
using namespace alloy;
using namespace alloy::compiler;
using namespace alloy::compiler::passes;
#include "xenia/profiling.h"
namespace alloy {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace alloy::hir;
using alloy::hir::HIRBuilder;
using alloy::hir::Instr;
using alloy::hir::Value;
DeadCodeEliminationPass::DeadCodeEliminationPass() :
CompilerPass() {
}
DeadCodeEliminationPass::DeadCodeEliminationPass() : CompilerPass() {}
DeadCodeEliminationPass::~DeadCodeEliminationPass() {
}
DeadCodeEliminationPass::~DeadCodeEliminationPass() {}
int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
SCOPE_profile_cpu_f("alloy");
@ -63,7 +67,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
bool any_instr_removed = false;
bool any_locals_removed = false;
Block* block = builder->first_block();
auto block = builder->first_block();
while (block) {
// Walk instructions in reverse.
Instr* i = block->instr_tail;
@ -71,8 +75,8 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
auto prev = i->prev;
auto opcode = i->opcode;
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) &&
i->dest && !i->dest->use_head) {
if (!(opcode->flags & OPCODE_FLAG_VOLATILE) && i->dest &&
!i->dest->use_head) {
// Has no uses and is not volatile. This instruction can die!
MakeNopRecursive(i);
any_instr_removed = true;
@ -110,7 +114,7 @@ int DeadCodeEliminationPass::Run(HIRBuilder* builder) {
// Remove all nops.
if (any_instr_removed) {
Block* block = builder->first_block();
block = builder->first_block();
while (block) {
Instr* i = block->instr_head;
while (i) {
@ -151,9 +155,9 @@ void DeadCodeEliminationPass::MakeNopRecursive(Instr* i) {
#define MAKE_NOP_SRC(n) \
if (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##.value = NULL; \
i->src##n.value = NULL; \
value->RemoveUse(use); \
if (!value->use_head) { \
/* Value is now unused, so recursively kill it. */ \
@ -190,7 +194,6 @@ void DeadCodeEliminationPass::ReplaceAssignment(Instr* i) {
}
bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
auto slot = i->src1.value;
auto src = i->src2.value;
auto use = src->use_head;
@ -209,3 +212,7 @@ bool DeadCodeEliminationPass::CheckLocalUse(Instr* i) {
return false;
}
} // namespace passes
} // namespace compiler
} // namespace alloy

View File

@ -10,7 +10,7 @@
#ifndef ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
#define ALLOY_COMPILER_PASSES_DEAD_CODE_ELIMINATION_PASS_H_
#include <alloy/compiler/compiler_pass.h>
#include "alloy/compiler/compiler_pass.h"
namespace alloy {
@ -21,9 +21,9 @@ namespace passes {
class DeadCodeEliminationPass : public CompilerPass {
public:
DeadCodeEliminationPass();
virtual ~DeadCodeEliminationPass();
~DeadCodeEliminationPass() override;
virtual int Run(hir::HIRBuilder* builder);
int Run(hir::HIRBuilder* builder) override;
private:
void MakeNopRecursive(hir::Instr* i);

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