mirror of https://github.com/inolen/redream.git
jit c refactor
This commit is contained in:
parent
37915e7129
commit
28533002af
|
@ -136,6 +136,7 @@ set(REDREAM_SOURCES
|
|||
src/core/interval_tree.c
|
||||
src/core/list.c
|
||||
src/core/log.c
|
||||
src/core/mm_heap.c
|
||||
src/core/option.c
|
||||
src/core/profiler.c
|
||||
src/core/rb_tree.c
|
||||
|
@ -160,22 +161,20 @@ set(REDREAM_SOURCES
|
|||
src/hw/memory.c
|
||||
src/hw/scheduler.c
|
||||
src/jit/backend/x64/x64_backend.cc
|
||||
src/jit/backend/x64/x64_disassembler.cc
|
||||
src/jit/backend/x64/x64_emitter.cc
|
||||
src/jit/frontend/sh4/sh4_analyzer.cc
|
||||
src/jit/frontend/sh4/sh4_builder.cc
|
||||
src/jit/frontend/sh4/sh4_disassembler.cc
|
||||
src/jit/frontend/sh4/sh4_frontend.cc
|
||||
src/jit/ir/ir_builder.cc
|
||||
src/jit/ir/ir_reader.cc
|
||||
src/jit/ir/ir_writer.cc
|
||||
src/jit/backend/x64/x64_disassembler.c
|
||||
src/jit/frontend/sh4/sh4_analyze.c
|
||||
src/jit/frontend/sh4/sh4_disasm.c
|
||||
src/jit/frontend/sh4/sh4_frontend.c
|
||||
src/jit/frontend/sh4/sh4_translate.c
|
||||
src/jit/ir/ir.c
|
||||
src/jit/ir/ir_read.c
|
||||
src/jit/ir/ir_write.c
|
||||
#src/jit/ir/passes/constant_propagation_pass.cc
|
||||
src/jit/ir/passes/conversion_elimination_pass.cc
|
||||
src/jit/ir/passes/dead_code_elimination_pass.cc
|
||||
src/jit/ir/passes/load_store_elimination_pass.cc
|
||||
src/jit/ir/passes/pass_runner.cc
|
||||
src/jit/ir/passes/pass_stats.cc
|
||||
src/jit/ir/passes/register_allocation_pass.cc
|
||||
src/jit/ir/passes/conversion_elimination_pass.c
|
||||
src/jit/ir/passes/dead_code_elimination_pass.c
|
||||
src/jit/ir/passes/load_store_elimination_pass.c
|
||||
src/jit/ir/passes/pass_stat.c
|
||||
src/jit/ir/passes/register_allocation_pass.c
|
||||
src/renderer/gl_backend.c
|
||||
src/sys/exception_handler.c
|
||||
src/sys/filesystem.c
|
||||
|
@ -379,8 +378,8 @@ set(RETEST_SOURCES
|
|||
#test/test_interval_tree.cc
|
||||
#test/test_intrusive_list.cc
|
||||
test/test_list.cc
|
||||
#test/test_dead_code_elimination_pass.cc
|
||||
#test/test_load_store_elimination_pass.cc
|
||||
test/test_dead_code_elimination_pass.cc
|
||||
test/test_load_store_elimination_pass.cc
|
||||
#test/test_minmax_heap.cc
|
||||
test/test_sh4.cc
|
||||
${asm_inc})
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef ARENA_H
|
||||
#define ARENA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "core/assert.h"
|
||||
|
||||
namespace re {
|
||||
|
||||
class Arena {
|
||||
struct Chunk {
|
||||
Chunk(int capacity) : capacity(capacity), head(0), next(nullptr) {
|
||||
buffer = (uint8_t *)malloc(capacity);
|
||||
}
|
||||
|
||||
~Chunk() {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
int capacity;
|
||||
uint8_t *buffer;
|
||||
int head;
|
||||
Chunk *next;
|
||||
};
|
||||
|
||||
public:
|
||||
Arena(int chunk_size)
|
||||
: chunk_size_(chunk_size), root_chunk_(nullptr), current_chunk_(nullptr) {
|
||||
current_chunk_ = root_chunk_ = new Chunk(chunk_size_);
|
||||
}
|
||||
|
||||
~Arena() {
|
||||
Chunk *chunk = root_chunk_;
|
||||
|
||||
while (chunk) {
|
||||
Chunk *next = chunk->next;
|
||||
delete chunk;
|
||||
chunk = next;
|
||||
}
|
||||
}
|
||||
|
||||
void *Alloc(int bytes) {
|
||||
CHECK_LE(bytes, chunk_size_,
|
||||
"Allocation of %zu bytes is greater than chunk size of %zu bytes",
|
||||
bytes, chunk_size_);
|
||||
|
||||
// alloc the next chunk if we're out of capacity
|
||||
if ((current_chunk_->capacity - current_chunk_->head) < bytes) {
|
||||
Chunk *next = current_chunk_->next;
|
||||
if (!next) {
|
||||
next = new Chunk(chunk_size_);
|
||||
current_chunk_ = next;
|
||||
}
|
||||
current_chunk_ = next;
|
||||
}
|
||||
|
||||
void *ptr = current_chunk_->buffer + current_chunk_->head;
|
||||
current_chunk_->head += bytes;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *Alloc() {
|
||||
return (T *)Alloc(sizeof(T));
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
current_chunk_ = root_chunk_;
|
||||
current_chunk_->head = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int chunk_size_;
|
||||
Chunk *root_chunk_, *current_chunk_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,93 +0,0 @@
|
|||
#ifndef ARRAY_H
|
||||
#define ARRAY_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace re {
|
||||
|
||||
template <typename T>
|
||||
class array {
|
||||
public:
|
||||
array(int size = 8) : data_(nullptr), size_(0), capacity_(0) {
|
||||
Resize(size);
|
||||
}
|
||||
~array() {
|
||||
free(data_);
|
||||
}
|
||||
|
||||
array(array const &) = delete;
|
||||
void operator=(array const &) = delete;
|
||||
|
||||
T &operator[](int i) {
|
||||
return data_[i];
|
||||
}
|
||||
T operator[](int i) const {
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
T *data() {
|
||||
return data_;
|
||||
}
|
||||
const T *data() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
T &front() {
|
||||
return data_[0];
|
||||
}
|
||||
T &back() {
|
||||
return data_[size_ - 1];
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return size_;
|
||||
}
|
||||
bool empty() const {
|
||||
return !!size_;
|
||||
}
|
||||
int capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
void Resize(int size) {
|
||||
Reserve(size);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
void Reserve(int cap) {
|
||||
if (capacity_ >= cap) {
|
||||
return;
|
||||
}
|
||||
|
||||
// grow capacity to be >= cap
|
||||
if (!capacity_) {
|
||||
capacity_ = 1;
|
||||
}
|
||||
while (capacity_ < cap) {
|
||||
capacity_ *= 2;
|
||||
}
|
||||
|
||||
data_ = reinterpret_cast<T *>(realloc(data_, capacity_ * sizeof(T)));
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void PushBack(T v) {
|
||||
data_[size_++] = v;
|
||||
}
|
||||
|
||||
void PopBack() {
|
||||
size_--;
|
||||
}
|
||||
|
||||
private:
|
||||
T *data_;
|
||||
int size_;
|
||||
int capacity_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,22 +6,29 @@
|
|||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const decltype(((type*)0)->member)* __mptr = (ptr); \
|
||||
(type*)((char*)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
#include <type_traits>
|
||||
|
||||
#define TYPEOF(n) typename std::remove_reference<decltype(n)>::type
|
||||
|
||||
#else
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const __typeof__(((type*)0)->member)* __mptr = (ptr); \
|
||||
(type*)((char*)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
#define TYPEOF(n) __typeof__(n)
|
||||
|
||||
#endif
|
||||
|
||||
#define SWAP(a, b) \
|
||||
do { \
|
||||
TYPEOF(a) tmp = (a); \
|
||||
(a) = (b); \
|
||||
(b) = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const TYPEOF(((type*)0)->member)* __mptr = (ptr); \
|
||||
(type*)((char*)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
|
||||
#define array_size(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
#define array_resize(arr, new_size) \
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
#ifndef DELEGATE_H
|
||||
#define DELEGATE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
#include "core/assert.h"
|
||||
|
||||
namespace re {
|
||||
|
||||
template <typename F>
|
||||
class delegate;
|
||||
|
||||
template <typename R, typename... A>
|
||||
class delegate<R(A...)> {
|
||||
typedef R (*thunk_type)(void *, A...);
|
||||
|
||||
template <typename T>
|
||||
struct const_member_data {
|
||||
T *callee;
|
||||
R (T::*func)(A...) const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct member_data {
|
||||
T *callee;
|
||||
R (T::*func)(A...);
|
||||
};
|
||||
|
||||
typedef R (*func_data)(A...);
|
||||
|
||||
public:
|
||||
delegate() : thunk_(nullptr), data_() {}
|
||||
|
||||
delegate(std::nullptr_t) : delegate() {}
|
||||
|
||||
template <typename T>
|
||||
delegate(T *callee, std::nullptr_t)
|
||||
: delegate() {}
|
||||
|
||||
template <typename T>
|
||||
delegate(T *callee, R (T::*func)(A...) const)
|
||||
: delegate() {
|
||||
static_assert(sizeof(const_member_data<T>) < sizeof(data_),
|
||||
"data not large enough to hold member function pointer");
|
||||
|
||||
thunk_ = reinterpret_cast<thunk_type>(&const_member_thunk<T>);
|
||||
|
||||
*reinterpret_cast<const_member_data<T> *>(data_) = {callee, func};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
delegate(T *callee, R (T::*func)(A...))
|
||||
: delegate() {
|
||||
static_assert(sizeof(member_data<T>) < sizeof(data_),
|
||||
"data not large enough to hold member function pointer");
|
||||
|
||||
thunk_ = reinterpret_cast<thunk_type>(&member_thunk<T>);
|
||||
|
||||
*reinterpret_cast<member_data<T> *>(data_) = {callee, func};
|
||||
}
|
||||
|
||||
delegate(R (*func)(A...)) : delegate() {
|
||||
thunk_ = reinterpret_cast<thunk_type>(&func_thunk);
|
||||
|
||||
*reinterpret_cast<func_data *>(data_) = func;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return !!thunk_;
|
||||
}
|
||||
|
||||
bool operator==(const delegate &rhs) const noexcept {
|
||||
return (thunk_ == rhs.thunk_) && !memcmp(data_, rhs.data_, sizeof(data_));
|
||||
}
|
||||
|
||||
bool operator!=(const delegate &rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
R operator()(A... args) {
|
||||
DCHECK(thunk_);
|
||||
return thunk_(data_, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static R const_member_thunk(const_member_data<T> *data, A... args) {
|
||||
DCHECK(data->callee && data->func);
|
||||
return (data->callee->*data->func)(args...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static R member_thunk(member_data<T> *data, A... args) {
|
||||
DCHECK(data->callee && data->func);
|
||||
return (data->callee->*data->func)(args...);
|
||||
}
|
||||
|
||||
static R func_thunk(func_data *data, A... args) {
|
||||
DCHECK(data);
|
||||
return (*data)(args...);
|
||||
}
|
||||
|
||||
thunk_type thunk_;
|
||||
uint8_t data_[32];
|
||||
};
|
||||
|
||||
template <typename T, typename R, typename... A>
|
||||
delegate<R(A...)> make_delegate(R (T::*func)(A...) const, T *callee) {
|
||||
return delegate<R(A...)>(callee, func);
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... A>
|
||||
delegate<R(A...)> make_delegate(R (T::*func)(A...), T *callee) {
|
||||
return delegate<R(A...)>(callee, func);
|
||||
}
|
||||
template <typename R, typename... A>
|
||||
delegate<R(A...)> make_delegate(R (*func)(A...)) {
|
||||
return delegate<R(A...)>(func);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,314 +0,0 @@
|
|||
#ifndef INTRUSIVE_LIST_H
|
||||
#define INTRUSIVE_LIST_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include "core/assert.h"
|
||||
|
||||
namespace re {
|
||||
|
||||
// Objects are directly stored in the instrusive container, not copies. Due to
|
||||
// this, the lifetime of the object is not bound to the container. It's up to
|
||||
// the caller to manage the lifetime of the object being stored.
|
||||
template <typename T>
|
||||
class IntrusiveListNode {
|
||||
template <typename>
|
||||
friend class IntrusiveList;
|
||||
|
||||
public:
|
||||
IntrusiveListNode() : prev_(nullptr), next_(nullptr) {}
|
||||
|
||||
T *prev() {
|
||||
return prev_;
|
||||
}
|
||||
const T *prev() const {
|
||||
return prev_;
|
||||
}
|
||||
|
||||
T *next() {
|
||||
return next_;
|
||||
}
|
||||
const T *next() const {
|
||||
return next_;
|
||||
}
|
||||
|
||||
private:
|
||||
T *prev_;
|
||||
T *next_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveList {
|
||||
// For the iterator, remember that a C++ iterator's range is [begin, end),
|
||||
// meaning the end iterator will be wrapping an invalid node.
|
||||
template <bool is_const_iterator, bool is_reverse_iterator>
|
||||
class shared_iterator
|
||||
: public std::iterator<std::bidirectional_iterator_tag, T> {
|
||||
friend class IntrusiveList;
|
||||
|
||||
typedef shared_iterator<is_const_iterator, is_reverse_iterator> self_type;
|
||||
typedef typename std::conditional<is_const_iterator, IntrusiveList const *,
|
||||
IntrusiveList *>::type list_pointer;
|
||||
typedef typename std::conditional<is_const_iterator, T const *, T *>::type
|
||||
pointer;
|
||||
|
||||
static const intptr_t sentinel_end = 0xdeadbeef;
|
||||
|
||||
public:
|
||||
// FIXME Can some of these nasty conditionals be removed?
|
||||
// is_reverse_iterator is known at compile time
|
||||
|
||||
self_type &operator++() {
|
||||
node_ = is_reverse_iterator ? node_->prev() : node_->next();
|
||||
// if we've reached the end of the list, move onto the sentinel node
|
||||
if (!node_) {
|
||||
node_ = reinterpret_cast<T *>(sentinel_end);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_type operator++(int) {
|
||||
self_type old = *this;
|
||||
++(*this);
|
||||
return old;
|
||||
}
|
||||
|
||||
self_type &operator--() {
|
||||
// if we're at the sentinel node, the previous node is the list's tail
|
||||
if (node_ == reinterpret_cast<T *>(sentinel_end)) {
|
||||
node_ = is_reverse_iterator ? list_->head() : list_->tail();
|
||||
} else {
|
||||
node_ = is_reverse_iterator ? node_->next() : node_->prev();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_type operator--(int) {
|
||||
self_type old = *this;
|
||||
--(*this);
|
||||
return old;
|
||||
}
|
||||
|
||||
pointer operator*() {
|
||||
return node_;
|
||||
}
|
||||
|
||||
pointer operator->() {
|
||||
return node_;
|
||||
}
|
||||
|
||||
bool operator==(const self_type &other) const {
|
||||
return node_ == other.node_;
|
||||
}
|
||||
|
||||
bool operator!=(const self_type &other) const {
|
||||
return !(other == *this);
|
||||
}
|
||||
|
||||
private:
|
||||
shared_iterator(list_pointer list, pointer node)
|
||||
: list_(list),
|
||||
node_(node ? node : reinterpret_cast<T *>(sentinel_end)) {}
|
||||
|
||||
list_pointer list_;
|
||||
pointer node_;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef shared_iterator<false, false> iterator;
|
||||
typedef shared_iterator<true, false> const_iterator;
|
||||
typedef shared_iterator<false, true> reverse_iterator;
|
||||
typedef shared_iterator<true, true> const_reverse_iterator;
|
||||
|
||||
// regular iterators
|
||||
const_iterator begin() const {
|
||||
return const_iterator(this, head_);
|
||||
}
|
||||
const_iterator end() const {
|
||||
return const_iterator(this, nullptr);
|
||||
}
|
||||
iterator begin() {
|
||||
return iterator(this, head_);
|
||||
}
|
||||
iterator end() {
|
||||
return iterator(this, nullptr);
|
||||
}
|
||||
|
||||
// reverse iterators
|
||||
const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(this, tail_);
|
||||
}
|
||||
const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(this, nullptr);
|
||||
}
|
||||
reverse_iterator rbegin() {
|
||||
return reverse_iterator(this, tail_);
|
||||
}
|
||||
reverse_iterator rend() {
|
||||
return reverse_iterator(this, nullptr);
|
||||
}
|
||||
|
||||
const T *head() const {
|
||||
return head_;
|
||||
}
|
||||
const T *tail() const {
|
||||
return tail_;
|
||||
}
|
||||
|
||||
T *head() {
|
||||
return head_;
|
||||
}
|
||||
T *tail() {
|
||||
return tail_;
|
||||
}
|
||||
|
||||
IntrusiveList() : head_(nullptr), tail_(nullptr) {}
|
||||
|
||||
void Prepend(T *v) {
|
||||
Insert(nullptr, v);
|
||||
}
|
||||
|
||||
void Append(T *v) {
|
||||
Insert(tail_, v);
|
||||
}
|
||||
|
||||
void Insert(T *after, T *v) {
|
||||
DCHECK_EQ(reinterpret_cast<T *>(NULL), v->prev_);
|
||||
DCHECK_EQ(reinterpret_cast<T *>(NULL), v->next_);
|
||||
|
||||
// if after is null, insert at head
|
||||
if (!after) {
|
||||
if (head_) {
|
||||
v->next_ = head_;
|
||||
v->next_->prev_ = v;
|
||||
}
|
||||
|
||||
head_ = v;
|
||||
} else {
|
||||
T *next = after->next_;
|
||||
|
||||
v->prev_ = after;
|
||||
v->prev_->next_ = v;
|
||||
|
||||
if (next) {
|
||||
v->next_ = next;
|
||||
v->next_->prev_ = v;
|
||||
} else {
|
||||
v->next_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tail_ || after == tail_) {
|
||||
tail_ = v;
|
||||
}
|
||||
}
|
||||
|
||||
void Remove(T *v) {
|
||||
if (v->prev_) {
|
||||
v->prev_->next_ = v->next_;
|
||||
} else {
|
||||
head_ = v->next_;
|
||||
}
|
||||
|
||||
if (v->next_) {
|
||||
v->next_->prev_ = v->prev_;
|
||||
} else {
|
||||
tail_ = v->prev_;
|
||||
}
|
||||
|
||||
v->prev_ = v->next_ = nullptr;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
head_ = tail_ = nullptr;
|
||||
}
|
||||
|
||||
// Implements the mergesort for linked lists as described at
|
||||
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||
template <class Compare>
|
||||
void Sort(Compare comp) {
|
||||
T *head = head_;
|
||||
T *tail = nullptr;
|
||||
int k = 1;
|
||||
|
||||
while (true) {
|
||||
int merges = 0;
|
||||
T *p = head;
|
||||
|
||||
head = nullptr;
|
||||
tail = nullptr;
|
||||
|
||||
while (p) {
|
||||
// track the number of lists merged this pass
|
||||
merges++;
|
||||
|
||||
// step q forward k places, tracking the size of p
|
||||
int psize = 0;
|
||||
int qsize = k;
|
||||
T *q = p;
|
||||
while (psize < k && q) {
|
||||
psize++;
|
||||
q = q->next_;
|
||||
}
|
||||
|
||||
// merge the list starting at p of length psize with the list starting
|
||||
// at q of at most, length qsize
|
||||
while (psize || (qsize && q)) {
|
||||
T *next;
|
||||
|
||||
if (!psize) {
|
||||
next = q;
|
||||
q = q->next_;
|
||||
qsize--;
|
||||
} else if (!qsize || !q) {
|
||||
next = p;
|
||||
p = p->next_;
|
||||
psize--;
|
||||
} else if (comp(q, p)) {
|
||||
next = q;
|
||||
q = q->next_;
|
||||
qsize--;
|
||||
} else {
|
||||
next = p;
|
||||
p = p->next_;
|
||||
psize--;
|
||||
}
|
||||
|
||||
// move merged node to tail
|
||||
if (!tail) {
|
||||
head = next;
|
||||
} else {
|
||||
tail->next_ = next;
|
||||
}
|
||||
next->prev_ = tail;
|
||||
tail = next;
|
||||
}
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
if (tail) {
|
||||
tail->next_ = nullptr;
|
||||
}
|
||||
|
||||
// if only 1 pair of lists was merged, this is the end
|
||||
if (merges <= 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
k *= 2;
|
||||
}
|
||||
|
||||
// update internal head and tail with sorted head and tail
|
||||
head_ = head;
|
||||
tail_ = tail;
|
||||
}
|
||||
|
||||
private:
|
||||
T *head_;
|
||||
T *tail_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,10 @@
|
|||
#include "core/assert.h"
|
||||
#include "core/list.h"
|
||||
|
||||
int list_empty(list_t *list) {
|
||||
return !list->head;
|
||||
}
|
||||
|
||||
void list_add(list_t *list, list_node_t *n) {
|
||||
list_add_after(list, list->tail, n);
|
||||
}
|
||||
|
@ -53,6 +57,82 @@ void list_clear(list_t *list) {
|
|||
list->head = list->tail = NULL;
|
||||
}
|
||||
|
||||
int list_empty(list_t *list) {
|
||||
return !list->head;
|
||||
// Implements the mergesort for linked lists as described at
|
||||
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||
void list_sort(list_t *list, list_node_cmp cmp) {
|
||||
list_node_t *head = list->head;
|
||||
list_node_t *tail = NULL;
|
||||
int k = 1;
|
||||
|
||||
while (true) {
|
||||
int merges = 0;
|
||||
list_node_t *p = head;
|
||||
|
||||
head = NULL;
|
||||
tail = NULL;
|
||||
|
||||
while (p) {
|
||||
// track the number of lists merged this pass
|
||||
merges++;
|
||||
|
||||
// step q forward k places, tracking the size of p
|
||||
int psize = 0;
|
||||
int qsize = k;
|
||||
list_node_t *q = p;
|
||||
while (psize < k && q) {
|
||||
psize++;
|
||||
q = q->next;
|
||||
}
|
||||
|
||||
// merge the list starting at p of length psize with the list starting
|
||||
// at q of at most, length qsize
|
||||
while (psize || (qsize && q)) {
|
||||
list_node_t *next;
|
||||
|
||||
if (!psize) {
|
||||
next = q;
|
||||
q = q->next;
|
||||
qsize--;
|
||||
} else if (!qsize || !q) {
|
||||
next = p;
|
||||
p = p->next;
|
||||
psize--;
|
||||
} else if (cmp(q, p) < 0) {
|
||||
next = q;
|
||||
q = q->next;
|
||||
qsize--;
|
||||
} else {
|
||||
next = p;
|
||||
p = p->next;
|
||||
psize--;
|
||||
}
|
||||
|
||||
// move merged node to tail
|
||||
if (!tail) {
|
||||
head = next;
|
||||
} else {
|
||||
tail->next = next;
|
||||
}
|
||||
next->prev = tail;
|
||||
tail = next;
|
||||
}
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
if (tail) {
|
||||
tail->next = NULL;
|
||||
}
|
||||
|
||||
// if only 1 pair of lists was merged, this is the end
|
||||
if (merges <= 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
k *= 2;
|
||||
}
|
||||
|
||||
// update internal head and tail with sorted head and tail
|
||||
list->head = head;
|
||||
list->tail = tail;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,14 @@ typedef struct list_s {
|
|||
list_node_t *tail;
|
||||
} list_t;
|
||||
|
||||
typedef int (*list_node_cmp)(const list_node_t *a, const list_node_t *b);
|
||||
|
||||
int list_empty(list_t *list);
|
||||
void list_add(list_t *list, list_node_t *n);
|
||||
void list_add_after(list_t *list, list_node_t *after, list_node_t *n);
|
||||
void list_remove(list_t *list, list_node_t *n);
|
||||
void list_clear(list_t *list);
|
||||
int list_empty(list_t *list);
|
||||
void list_sort(list_t *list, list_node_cmp cmp);
|
||||
|
||||
#define list_for_each(list, it) \
|
||||
for (list_node_t *it = (list)->head, *it##_next = it ? it->next : NULL; it; \
|
||||
|
@ -32,48 +35,41 @@ int list_empty(list_t *list);
|
|||
|
||||
#define list_entry(n, type, member) container_of(n, type, member)
|
||||
|
||||
#define list_add_after_entry(list, after, member, n) \
|
||||
list_add_after(list, (after) ? &(after)->member : NULL, &(n)->member)
|
||||
|
||||
#define list_first_entry(list, type, member) \
|
||||
((list)->head ? list_entry((list)->head, type, member) : NULL)
|
||||
|
||||
#define list_last_entry(list, type, member) \
|
||||
((list)->tail ? list_entry((list)->tail, type, member) : NULL)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define list_next_entry(n, member) \
|
||||
((n) && (n)->member.next \
|
||||
? list_entry((n)->member.next, \
|
||||
std::remove_reference<decltype(*(n))>::type, member) \
|
||||
#define list_next_entry(n, member) \
|
||||
((n) && (n)->member.next \
|
||||
? list_entry((n)->member.next, TYPEOF(*(n)), member) \
|
||||
: NULL)
|
||||
|
||||
#define list_prev_entry(n, member) \
|
||||
((n) && (n)->member.prev \
|
||||
? list_entry((n)->member.prev, \
|
||||
std::remove_reference<decltype(*(n))>::type, member) \
|
||||
#define list_prev_entry(n, member) \
|
||||
((n) && (n)->member.prev \
|
||||
? list_entry((n)->member.prev, TYPEOF(*(n)), member) \
|
||||
: NULL)
|
||||
|
||||
#else
|
||||
#define list_for_each_entry(it, list, type, member) \
|
||||
for (type *it = list_first_entry(list, type, member); it; \
|
||||
it = list_next_entry(it, member))
|
||||
|
||||
#define list_next_entry(n, member) \
|
||||
((n) && (n)->member.next \
|
||||
? list_entry((n)->member.next, __typeof__(*(n)), member) \
|
||||
: NULL)
|
||||
|
||||
#define list_prev_entry(n, member) \
|
||||
((n) && (n)->member.prev \
|
||||
? list_entry((n)->member.prev, __typeof__(*(n)), member) \
|
||||
: NULL)
|
||||
|
||||
#endif
|
||||
|
||||
#define list_for_each_entry(list, type, member, it) \
|
||||
for (type *it = list_first_entry(list, type, member), \
|
||||
*it##_next = list_next_entry(it, member); \
|
||||
#define list_for_each_entry_safe(it, list, type, member) \
|
||||
for (type *it = list_first_entry(list, type, member), \
|
||||
*it##_next = list_next_entry(it, member); \
|
||||
it; it = it##_next, it##_next = list_next_entry(it, member))
|
||||
|
||||
#define list_for_each_entry_reverse(list, type, member, it) \
|
||||
for (type *it = list_last_entry(list, type, member), \
|
||||
*it##_next = list_prev_entry(it, member); \
|
||||
#define list_for_each_entry_reverse(it, list, type, member) \
|
||||
for (type *it = list_last_entry(list, type, member); it; \
|
||||
it = list_prev_entry(it, member))
|
||||
|
||||
#define list_for_each_entry_safe_reverse(it, list, type, member) \
|
||||
for (type *it = list_last_entry(list, type, member), \
|
||||
*it##_next = list_prev_entry(it, member); \
|
||||
it; it = it##_next, it##_next = list_prev_entry(it, member))
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef REDREAM_MEMORY_H
|
||||
#define REDREAM_MEMORY_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#define alloca _alloca
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
T load(const void *ptr) {
|
||||
return *reinterpret_cast<const T *>(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void store(void *ptr, T v) {
|
||||
*reinterpret_cast<T *>(ptr) = v;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,288 +0,0 @@
|
|||
#ifndef MINMAX_HEAP_H
|
||||
#define MINMAX_HEAP_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "core/assert.h"
|
||||
|
||||
// Min-max heap implementation, based on
|
||||
// http://www.akira.ruc.dk/~keld/teaching/algoritmedesign_f03/Artikler/02../Atkinson86.pdf
|
||||
|
||||
namespace re {
|
||||
|
||||
template <typename T>
|
||||
static inline bool mmheap_is_max_level(T index) {
|
||||
T n = index + 1;
|
||||
T log2 = 0;
|
||||
while (n >>= 1) log2++;
|
||||
return log2 % 2 == 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T mmheap_parent(T index) {
|
||||
return (index - 1) / 2;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T mmheap_grandparent(T index) {
|
||||
return mmheap_parent(mmheap_parent(index));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool mmheap_has_grandparent(T index) {
|
||||
return mmheap_parent(index) != 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T mmheap_left_child(T index) {
|
||||
return 2 * index + 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T mmheap_left_grandchild(T index) {
|
||||
return mmheap_left_child(mmheap_left_child(index));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T mmheap_is_child(T parent, T child) {
|
||||
return parent == ((child - 1) / 2);
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Compare>
|
||||
void mmheap_sift_up(
|
||||
RandomIt first, RandomIt last, Compare comp,
|
||||
typename std::iterator_traits<RandomIt>::difference_type index) {
|
||||
using difference_type =
|
||||
typename std::iterator_traits<RandomIt>::difference_type;
|
||||
|
||||
// can't sift up past the root
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
|
||||
difference_type ancestor_index = mmheap_parent(index);
|
||||
bool max_level = mmheap_is_max_level(ancestor_index);
|
||||
|
||||
// if the node is smaller (greater) than its parent, then it is smaller
|
||||
// (greater) than all other nodes at max (min) levels up to the root. swap
|
||||
// the node with its parent and check min (max) levels up to the root until
|
||||
// the min-max order property is satisfied
|
||||
if (comp(*(first + index), *(first + ancestor_index)) ^ max_level) {
|
||||
std::swap(*(first + ancestor_index), *(first + index));
|
||||
index = ancestor_index;
|
||||
}
|
||||
// if the node is greater (smaller) than its parent, then it is greater
|
||||
// (smaller) than all other nodes at min (max) levels up to the root. the
|
||||
// node is in the correct order with regards to its parent, but check max
|
||||
// (min) levels up to the root until the min-max order property is satisfied
|
||||
else {
|
||||
max_level = !max_level;
|
||||
}
|
||||
|
||||
while (mmheap_has_grandparent(index)) {
|
||||
ancestor_index = mmheap_grandparent(index);
|
||||
|
||||
// once node is greater (smaller) than parent, the min-max order property
|
||||
// is satisfied
|
||||
if (!(comp(*(first + index), *(first + ancestor_index)) ^ max_level)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap node with parent
|
||||
std::swap(*(first + ancestor_index), *(first + index));
|
||||
index = ancestor_index;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Compare>
|
||||
void mmheap_sift_down(
|
||||
RandomIt first, RandomIt last, Compare comp,
|
||||
typename std::iterator_traits<RandomIt>::difference_type index) {
|
||||
using difference_type =
|
||||
typename std::iterator_traits<RandomIt>::difference_type;
|
||||
|
||||
bool max_level = mmheap_is_max_level(index);
|
||||
difference_type size = last - first;
|
||||
|
||||
while (index < size) {
|
||||
// get the smallest (largest) child or grandchild
|
||||
difference_type smallest = index;
|
||||
|
||||
difference_type i = mmheap_left_child(index);
|
||||
difference_type end = std::min(i + 2, size);
|
||||
for (; i < end; i++) {
|
||||
if (comp(*(first + i), *(first + smallest)) ^ max_level) {
|
||||
smallest = i;
|
||||
}
|
||||
}
|
||||
|
||||
i = mmheap_left_grandchild(index);
|
||||
end = std::min(i + 4, size);
|
||||
for (; i < end; i++) {
|
||||
if (comp(*(first + i), *(first + smallest)) ^ max_level) {
|
||||
smallest = i;
|
||||
}
|
||||
}
|
||||
|
||||
// already the smallest (largest) node, nothing to do
|
||||
if (smallest == index) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap the node with the smallest (largest) descendant
|
||||
std::swap(*(first + index), *(first + smallest));
|
||||
|
||||
// if the swapped node was a child, then the current node, its child, and
|
||||
// its grandchild are all ordered correctly at this point satisfying the
|
||||
// min-max order property
|
||||
if (mmheap_is_child(index, smallest)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if the node's new parent is now smaller than it, swap again
|
||||
if (comp(*(first + mmheap_parent(smallest)), *(first + smallest)) ^
|
||||
max_level) {
|
||||
std::swap(*(first + mmheap_parent(smallest)), *(first + smallest));
|
||||
}
|
||||
|
||||
// if the swapped node was a grandchild, iteration must continue to
|
||||
// ensure it's now ordered with regard to its descendants
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
bool mmheap_validate(RandomIt first, RandomIt last, Comp comp) {
|
||||
using difference_type =
|
||||
typename std::iterator_traits<RandomIt>::difference_type;
|
||||
|
||||
difference_type size = last - first;
|
||||
|
||||
for (difference_type i = 0; i < size; i++) {
|
||||
bool flip_compare = mmheap_is_max_level(i);
|
||||
|
||||
// values stored at nodes on even (odd) levels are smaller (greater) than
|
||||
// or equal to the values stored at their descendants
|
||||
|
||||
// validate children
|
||||
difference_type j = std::min(mmheap_left_child(i), size);
|
||||
difference_type end = std::min(j + 2, size);
|
||||
|
||||
for (; j < end; j++) {
|
||||
if (!(comp(*(first + i), *(first + j)) ^ flip_compare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// validate grandchildren
|
||||
j = std::min(mmheap_left_grandchild(i), size);
|
||||
end = std::min(j + 4, size);
|
||||
|
||||
for (; j < end; j++) {
|
||||
if (!(comp(*(first + i), *(first + j)) ^ flip_compare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
bool mmheap_validate(RandomIt first, RandomIt last) {
|
||||
return mmheap_validate(
|
||||
first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
void mmheap_push(RandomIt first, RandomIt last, Comp comp) {
|
||||
mmheap_sift_up(first, last, comp, (last - first) - 1);
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
void mmheap_push(RandomIt first, RandomIt last) {
|
||||
mmheap_push(first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
RandomIt mmheap_find_min(RandomIt first, RandomIt last, Comp comp) {
|
||||
return first;
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
RandomIt mmheap_find_min(RandomIt first, RandomIt last) {
|
||||
return mmheap_find_min(
|
||||
first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
RandomIt mmheap_find_max(RandomIt first, RandomIt last, Comp comp) {
|
||||
using difference_type =
|
||||
typename std::iterator_traits<RandomIt>::difference_type;
|
||||
|
||||
difference_type size = last - first;
|
||||
|
||||
if (size == 1) {
|
||||
// root must be the max
|
||||
return first;
|
||||
} else if (size == 2) {
|
||||
// root's child must be the max
|
||||
return first + 1;
|
||||
} else {
|
||||
// must be the larger of the two children
|
||||
if (comp(*(first + 1), *(first + 2))) {
|
||||
return first + 2;
|
||||
} else {
|
||||
return first + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
RandomIt mmheap_find_max(RandomIt first, RandomIt last) {
|
||||
return mmheap_find_max(
|
||||
first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
void mmheap_pop_min(RandomIt first, RandomIt last, Comp comp) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
RandomIt min = mmheap_find_min(first, last, comp);
|
||||
std::swap(*min, *--last);
|
||||
mmheap_sift_down(first, last, comp, std::distance(first, min));
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
void mmheap_pop_min(RandomIt first, RandomIt last) {
|
||||
mmheap_pop_min(
|
||||
first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
|
||||
template <typename RandomIt, typename Comp>
|
||||
void mmheap_pop_max(RandomIt first, RandomIt last, Comp comp) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
RandomIt max = mmheap_find_max(first, last, comp);
|
||||
std::swap(*max, *--last);
|
||||
mmheap_sift_down(first, last, comp, std::distance(first, max));
|
||||
}
|
||||
|
||||
template <typename RandomIt>
|
||||
void mmheap_pop_max(RandomIt first, RandomIt last) {
|
||||
mmheap_pop_max(
|
||||
first, last,
|
||||
std::less<typename std::iterator_traits<RandomIt>::value_type>());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,200 @@
|
|||
#include "core/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/mm_heap.h"
|
||||
|
||||
static inline bool mm_is_max_level(int index) {
|
||||
int n = index + 1;
|
||||
int log2 = 0;
|
||||
while (n >>= 1) log2++;
|
||||
return log2 % 2 == 1;
|
||||
}
|
||||
|
||||
static inline int mm_parent(int index) {
|
||||
return (index - 1) / 2;
|
||||
}
|
||||
|
||||
static inline int mm_grandparent(int index) {
|
||||
return mm_parent(mm_parent(index));
|
||||
}
|
||||
|
||||
static inline bool mm_has_grandparent(int index) {
|
||||
return mm_parent(index) != 0;
|
||||
}
|
||||
|
||||
static inline int mm_left_child(int index) {
|
||||
return 2 * index + 1;
|
||||
}
|
||||
|
||||
static inline int mm_left_grandchild(int index) {
|
||||
return mm_left_child(mm_left_child(index));
|
||||
}
|
||||
|
||||
static inline int mm_is_child(int parent, int child) {
|
||||
return parent == ((child - 1) / 2);
|
||||
}
|
||||
|
||||
static void mm_sift_up(mm_type *begin, int size, int index, mm_cmp cmp) {
|
||||
// can't sift up past the root
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ancestor_index = mm_parent(index);
|
||||
bool max_level = mm_is_max_level(ancestor_index);
|
||||
|
||||
// if the node is smaller (greater) than its parent, then it is smaller
|
||||
// (greater) than all other nodes at max (min) levels up to the root. swap
|
||||
// the node with its parent and check min (max) levels up to the root until
|
||||
// the min-max order property is satisfied
|
||||
if (cmp(*(begin + index), *(begin + ancestor_index)) ^ max_level) {
|
||||
SWAP(*(begin + ancestor_index), *(begin + index));
|
||||
index = ancestor_index;
|
||||
}
|
||||
// if the node is greater (smaller) than its parent, then it is greater
|
||||
// (smaller) than all other nodes at min (max) levels up to the root. the
|
||||
// node is in the correct order with regards to its parent, but check max
|
||||
// (min) levels up to the root until the min-max order property is satisfied
|
||||
else {
|
||||
max_level = !max_level;
|
||||
}
|
||||
|
||||
while (mm_has_grandparent(index)) {
|
||||
ancestor_index = mm_grandparent(index);
|
||||
|
||||
// once node is greater (smaller) than parent, the min-max order property
|
||||
// is satisfied
|
||||
if (!(cmp(*(begin + index), *(begin + ancestor_index)) ^ max_level)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap node with parent
|
||||
SWAP(*(begin + ancestor_index), *(begin + index));
|
||||
index = ancestor_index;
|
||||
}
|
||||
}
|
||||
|
||||
static void mm_sift_down(mm_type *begin, int size, int index, mm_cmp cmp) {
|
||||
bool max_level = mm_is_max_level(index);
|
||||
|
||||
while (index < size) {
|
||||
// get the smallest (largest) child or grandchild
|
||||
int smallest = index;
|
||||
|
||||
int i = mm_left_child(index);
|
||||
int end = MIN(i + 2, size);
|
||||
for (; i < end; i++) {
|
||||
if (cmp(*(begin + i), *(begin + smallest)) ^ max_level) {
|
||||
smallest = i;
|
||||
}
|
||||
}
|
||||
|
||||
i = mm_left_grandchild(index);
|
||||
end = MIN(i + 4, size);
|
||||
for (; i < end; i++) {
|
||||
if (cmp(*(begin + i), *(begin + smallest)) ^ max_level) {
|
||||
smallest = i;
|
||||
}
|
||||
}
|
||||
|
||||
// already the smallest (largest) node, nothing to do
|
||||
if (smallest == index) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap the node with the smallest (largest) descendant
|
||||
SWAP(*(begin + index), *(begin + smallest));
|
||||
|
||||
// if the swapped node was a child, then the current node, its child, and
|
||||
// its grandchild are all ordered correctly at this point satisfying the
|
||||
// min-max order property
|
||||
if (mm_is_child(index, smallest)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if the node's new parent is now smaller than it, swap again
|
||||
int parent = mm_parent(smallest);
|
||||
if (cmp(*(begin + parent), *(begin + smallest)) ^ max_level) {
|
||||
SWAP(*(begin + parent), *(begin + smallest));
|
||||
}
|
||||
|
||||
// if the swapped node was a grandchild, iteration must continue to
|
||||
// ensure it's now ordered with regard to its descendants
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
bool mm_validate(mm_type *begin, int size, mm_cmp cmp) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
bool flip_compare = mm_is_max_level(i);
|
||||
|
||||
// values stored at nodes on even (odd) levels are smaller (greater) than
|
||||
// or equal to the values stored at their descendants
|
||||
|
||||
// validate children
|
||||
int j = MIN(mm_left_child(i), size);
|
||||
int end = MIN(j + 2, size);
|
||||
|
||||
for (; j < end; j++) {
|
||||
if (!(cmp(*(begin + i), *(begin + j)) ^ flip_compare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// validate grandchildren
|
||||
j = MIN(mm_left_grandchild(i), size);
|
||||
end = MIN(j + 4, size);
|
||||
|
||||
for (; j < end; j++) {
|
||||
if (!(cmp(*(begin + i), *(begin + j)) ^ flip_compare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mm_push(mm_type *begin, int size, mm_cmp cmp) {
|
||||
mm_sift_up(begin, size, size - 1, cmp);
|
||||
}
|
||||
|
||||
mm_type *mm_find_min(mm_type *begin, int size, mm_cmp cmp) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
mm_type *mm_find_max(mm_type *begin, int size, mm_cmp cmp) {
|
||||
if (size == 1) {
|
||||
// root must be the max
|
||||
return begin;
|
||||
} else if (size == 2) {
|
||||
// root's child must be the max
|
||||
return begin + 1;
|
||||
} else {
|
||||
// must be the larger of the two children
|
||||
if (cmp(*(begin + 1), *(begin + 2))) {
|
||||
return begin + 2;
|
||||
} else {
|
||||
return begin + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mm_pop_min(mm_type *begin, int size, mm_cmp cmp) {
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
mm_type *min = mm_find_min(begin, size, cmp);
|
||||
SWAP(*min, *(begin + size - 1));
|
||||
mm_sift_down(begin, size - 1, min - begin, cmp);
|
||||
}
|
||||
|
||||
void mm_pop_max(mm_type *begin, int size, mm_cmp cmp) {
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
mm_type *max = mm_find_max(begin, size, cmp);
|
||||
SWAP(*max, *(begin + size - 1));
|
||||
mm_sift_down(begin, size - 1, max - begin, cmp);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef MM_HEAP_H
|
||||
#define MM_HEAP_H
|
||||
|
||||
// Min-max heap implementation, based on
|
||||
// http://www.akira.ruc.dk/~keld/teaching/algoritmedesign_f03/Artikler/02../Atkinson86.pdf
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *mm_type;
|
||||
typedef bool (*mm_cmp)(mm_type lhs, mm_type rhs);
|
||||
|
||||
bool mm_validate(mm_type *begin, int size, mm_cmp cmp);
|
||||
void mm_push(mm_type *begin, int size, mm_cmp cmp);
|
||||
mm_type *mm_find_min(mm_type *begin, int size, mm_cmp cmp);
|
||||
mm_type *mm_find_max(mm_type *begin, int size, mm_cmp cmp);
|
||||
void mm_pop_min(mm_type *begin, int size, mm_cmp cmp);
|
||||
void mm_pop_max(mm_type *begin, int size, mm_cmp cmp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -6,7 +6,7 @@
|
|||
static list_t s_options;
|
||||
|
||||
static option_t *option_find(const char *name) {
|
||||
list_for_each_entry(&s_options, option_t, it, opt) {
|
||||
list_for_each_entry(opt, &s_options, option_t, it) {
|
||||
if (!strcmp(opt->name, name)) {
|
||||
return opt;
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ void option_print_help() {
|
|||
int max_name_width = 0;
|
||||
int max_desc_width = 0;
|
||||
|
||||
list_for_each_entry(&s_options, option_t, it, opt) {
|
||||
list_for_each_entry(opt, &s_options, option_t, it) {
|
||||
int l = (int)strlen(opt->name);
|
||||
max_name_width = MAX(l, max_name_width);
|
||||
|
||||
|
@ -91,7 +91,7 @@ void option_print_help() {
|
|||
max_desc_width = MAX(l, max_desc_width);
|
||||
}
|
||||
|
||||
list_for_each_entry(&s_options, option_t, it, opt) {
|
||||
list_for_each_entry(opt, &s_options, option_t, it) {
|
||||
switch (opt->type) {
|
||||
case OPT_BOOL:
|
||||
LOG_INFO("--%-*s %-*s [default %s]", max_name_width, opt->name,
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "core/core.h"
|
||||
|
||||
#define RB_NODE(n) ((rb_node_t *)n)
|
||||
|
||||
typedef enum {
|
||||
|
@ -46,26 +48,12 @@ rb_node_t *rb_next(rb_node_t *n);
|
|||
|
||||
#define rb_entry(n, type, member) container_of(n, type, member)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define rb_find_entry(t, search, member, cb) \
|
||||
({ \
|
||||
rb_node_t *it = rb_find(t, &(search)->member, cb); \
|
||||
it ? rb_entry(it, std::remove_reference<decltype(*(search))>::type, \
|
||||
member) \
|
||||
: NULL; \
|
||||
#define rb_find_entry(t, search, member, cb) \
|
||||
({ \
|
||||
rb_node_t *it = rb_find(t, &(search)->member, cb); \
|
||||
it ? rb_entry(it, TYPEOF(*search), member) : NULL; \
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#define rb_find_entry(t, search, member, cb) \
|
||||
({ \
|
||||
rb_node_t *it = rb_find(t, &(search)->member, cb); \
|
||||
it ? rb_entry(it, __typeof__(*search), member) : NULL; \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
// #define rb_for_each_entry(t, member, it) \
|
||||
// for (rb_node_t *it = rb_first(t), *it##_next = rb_next(it); it; \
|
||||
// it = it##_next, it##_next = rb_next(it))
|
||||
|
|
|
@ -548,7 +548,8 @@ void ta_clear_textures(ta_t *ta) {
|
|||
}
|
||||
|
||||
void ta_clear_pending_textures(ta_t *ta) {
|
||||
list_for_each_entry(&ta->invalid_entries, texture_entry_t, invalid_it, it) {
|
||||
list_for_each_entry_safe(it, &ta->invalid_entries, texture_entry_t,
|
||||
invalid_it) {
|
||||
ta_invalidate_texture(ta, it);
|
||||
ta->num_invalidated++;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "core/log.h"
|
||||
#include "core/option.h"
|
||||
#include "core/string.h"
|
||||
#include "hw/maple/controller.h"
|
||||
#include "hw/maple/maple.h"
|
||||
|
||||
DEFINE_OPTION_STRING(profile, "profiles/ps4.ini", "Controller profile");
|
||||
|
@ -60,6 +59,115 @@ static maple_deviceinfo_t controller_devinfo = {
|
|||
0x01ae,
|
||||
0x01f4};
|
||||
|
||||
static void controller_load_profile(controller_t *ctrl, const char *path);
|
||||
static int controller_ini_handler(void *user, const char *section,
|
||||
const char *name, const char *value);
|
||||
static void controller_destroy(controller_t *controller);
|
||||
static bool controller_input(controller_t *ctrl, keycode_t key, int16_t value);
|
||||
static bool controller_frame(controller_t *ctrl, const maple_frame_t *frame,
|
||||
maple_frame_t *res);
|
||||
|
||||
maple_device_t *controller_create() {
|
||||
controller_t *ctrl = calloc(1, sizeof(controller_t));
|
||||
ctrl->base.destroy = (maple_destroy_cb)&controller_destroy;
|
||||
ctrl->base.input = (maple_input_cb)&controller_input;
|
||||
ctrl->base.frame = (maple_frame_cb)&controller_frame;
|
||||
ctrl->cnd.function = FN_CONTROLLER;
|
||||
|
||||
// buttons bitfield contains 0s for pressed buttons and 1s for unpressed
|
||||
ctrl->cnd.buttons = 0xffff;
|
||||
|
||||
// triggers completely unpressed
|
||||
ctrl->cnd.rtrig = ctrl->cnd.ltrig = 0;
|
||||
|
||||
// joysticks default to dead center
|
||||
ctrl->cnd.joyy = ctrl->cnd.joyx = ctrl->cnd.joyx2 = ctrl->cnd.joyy2 = 0x80;
|
||||
|
||||
// default profile
|
||||
// CONT_JOYX
|
||||
// CONT_JOYY
|
||||
// CONT_LTRIG
|
||||
// CONT_RTRIG
|
||||
ctrl->map[K_SPACE] = CONT_START;
|
||||
ctrl->map[(keycode_t)'k'] = CONT_A;
|
||||
ctrl->map[(keycode_t)'l'] = CONT_B;
|
||||
ctrl->map[(keycode_t)'j'] = CONT_X;
|
||||
ctrl->map[(keycode_t)'i'] = CONT_Y;
|
||||
ctrl->map[(keycode_t)'w'] = CONT_DPAD_UP;
|
||||
ctrl->map[(keycode_t)'s'] = CONT_DPAD_DOWN;
|
||||
ctrl->map[(keycode_t)'a'] = CONT_DPAD_LEFT;
|
||||
ctrl->map[(keycode_t)'d'] = CONT_DPAD_RIGHT;
|
||||
|
||||
// load profile
|
||||
controller_load_profile(ctrl, OPTION_profile);
|
||||
|
||||
return &ctrl->base;
|
||||
}
|
||||
|
||||
static void controller_load_profile(controller_t *ctrl, const char *path) {
|
||||
if (!*path) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Loading controller profile %s", path);
|
||||
|
||||
if (ini_parse(path, controller_ini_handler, ctrl) < 0) {
|
||||
LOG_WARNING("Failed to parse %s", path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int controller_ini_handler(void *user, const char *section,
|
||||
const char *name, const char *value) {
|
||||
controller_t *ctrl = user;
|
||||
|
||||
int button = 0;
|
||||
if (!strcmp(name, "joyx")) {
|
||||
button = CONT_JOYX;
|
||||
} else if (!strcmp(name, "joyy")) {
|
||||
button = CONT_JOYY;
|
||||
} else if (!strcmp(name, "ltrig")) {
|
||||
button = CONT_LTRIG;
|
||||
} else if (!strcmp(name, "rtrig")) {
|
||||
button = CONT_RTRIG;
|
||||
} else if (!strcmp(name, "start")) {
|
||||
button = CONT_START;
|
||||
} else if (!strcmp(name, "a")) {
|
||||
button = CONT_A;
|
||||
} else if (!strcmp(name, "b")) {
|
||||
button = CONT_B;
|
||||
} else if (!strcmp(name, "x")) {
|
||||
button = CONT_X;
|
||||
} else if (!strcmp(name, "y")) {
|
||||
button = CONT_Y;
|
||||
} else if (!strcmp(name, "dpad_up")) {
|
||||
button = CONT_DPAD_UP;
|
||||
} else if (!strcmp(name, "dpad_down")) {
|
||||
button = CONT_DPAD_DOWN;
|
||||
} else if (!strcmp(name, "dpad_left")) {
|
||||
button = CONT_DPAD_LEFT;
|
||||
} else if (!strcmp(name, "dpad_right")) {
|
||||
button = CONT_DPAD_RIGHT;
|
||||
} else {
|
||||
LOG_WARNING("Unknown button %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
keycode_t key = get_key_by_name(value);
|
||||
if (key == K_UNKNOWN) {
|
||||
LOG_WARNING("Unknown key %s", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctrl->map[key] = button;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void controller_destroy(controller_t *controller) {
|
||||
free(controller);
|
||||
}
|
||||
|
||||
static bool controller_input(controller_t *ctrl, keycode_t key, int16_t value) {
|
||||
// map incoming key to dreamcast button
|
||||
int button = ctrl->map[key];
|
||||
|
@ -113,99 +221,3 @@ static bool controller_frame(controller_t *ctrl, const maple_frame_t *frame,
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int controler_ini_handler(void *user, const char *section,
|
||||
const char *name, const char *value) {
|
||||
controller_t *ctrl = user;
|
||||
|
||||
int button = 0;
|
||||
if (!strcmp(name, "joyx")) {
|
||||
button = CONT_JOYX;
|
||||
} else if (!strcmp(name, "joyy")) {
|
||||
button = CONT_JOYY;
|
||||
} else if (!strcmp(name, "ltrig")) {
|
||||
button = CONT_LTRIG;
|
||||
} else if (!strcmp(name, "rtrig")) {
|
||||
button = CONT_RTRIG;
|
||||
} else if (!strcmp(name, "start")) {
|
||||
button = CONT_START;
|
||||
} else if (!strcmp(name, "a")) {
|
||||
button = CONT_A;
|
||||
} else if (!strcmp(name, "b")) {
|
||||
button = CONT_B;
|
||||
} else if (!strcmp(name, "x")) {
|
||||
button = CONT_X;
|
||||
} else if (!strcmp(name, "y")) {
|
||||
button = CONT_Y;
|
||||
} else if (!strcmp(name, "dpad_up")) {
|
||||
button = CONT_DPAD_UP;
|
||||
} else if (!strcmp(name, "dpad_down")) {
|
||||
button = CONT_DPAD_DOWN;
|
||||
} else if (!strcmp(name, "dpad_left")) {
|
||||
button = CONT_DPAD_LEFT;
|
||||
} else if (!strcmp(name, "dpad_right")) {
|
||||
button = CONT_DPAD_RIGHT;
|
||||
} else {
|
||||
LOG_WARNING("Unknown button %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
keycode_t key = get_key_by_name(value);
|
||||
if (key == K_UNKNOWN) {
|
||||
LOG_WARNING("Unknown key %s", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctrl->map[key] = button;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void controller_load_profile(controller_t *ctrl, const char *path) {
|
||||
if (!*path) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Loading controller profile %s", path);
|
||||
|
||||
if (ini_parse(path, controler_ini_handler, ctrl) < 0) {
|
||||
LOG_WARNING("Failed to parse %s", path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct maple_device_s *maple_create_controller() {
|
||||
controller_t *ctrl = calloc(1, sizeof(controller_t));
|
||||
ctrl->base.input = (maple_input_cb)&controller_input;
|
||||
ctrl->base.frame = (maple_frame_cb)&controller_frame;
|
||||
ctrl->cnd.function = FN_CONTROLLER;
|
||||
|
||||
// buttons bitfield contains 0s for pressed buttons and 1s for unpressed
|
||||
ctrl->cnd.buttons = 0xffff;
|
||||
|
||||
// triggers completely unpressed
|
||||
ctrl->cnd.rtrig = ctrl->cnd.ltrig = 0;
|
||||
|
||||
// joysticks default to dead center
|
||||
ctrl->cnd.joyy = ctrl->cnd.joyx = ctrl->cnd.joyx2 = ctrl->cnd.joyy2 = 0x80;
|
||||
|
||||
// default profile
|
||||
// CONT_JOYX
|
||||
// CONT_JOYY
|
||||
// CONT_LTRIG
|
||||
// CONT_RTRIG
|
||||
ctrl->map[K_SPACE] = CONT_START;
|
||||
ctrl->map[(keycode_t)'k'] = CONT_A;
|
||||
ctrl->map[(keycode_t)'l'] = CONT_B;
|
||||
ctrl->map[(keycode_t)'j'] = CONT_X;
|
||||
ctrl->map[(keycode_t)'i'] = CONT_Y;
|
||||
ctrl->map[(keycode_t)'w'] = CONT_DPAD_UP;
|
||||
ctrl->map[(keycode_t)'s'] = CONT_DPAD_DOWN;
|
||||
ctrl->map[(keycode_t)'a'] = CONT_DPAD_LEFT;
|
||||
ctrl->map[(keycode_t)'d'] = CONT_DPAD_RIGHT;
|
||||
|
||||
// load profile
|
||||
controller_load_profile(ctrl, OPTION_profile);
|
||||
|
||||
return &ctrl->base;
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef CONTROLLER_H
|
||||
#define CONTROLLER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct maple_device_s;
|
||||
|
||||
struct maple_device_s *maple_create_controller();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,6 +1,5 @@
|
|||
#include "hw/holly/holly.h"
|
||||
#include "hw/maple/maple.h"
|
||||
#include "hw/maple/controller.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
|
||||
|
@ -20,13 +19,13 @@ static void maple_dma(maple_t *mp);
|
|||
static void maple_keydown(maple_t *mp, keycode_t key, int16_t value);
|
||||
DECLARE_REG_W32(maple_t *mp, SB_MDST);
|
||||
|
||||
struct maple_s *maple_create(struct dreamcast_s *dc) {
|
||||
maple_t *maple_create(struct dreamcast_s *dc) {
|
||||
maple_t *mp = dc_create_device(dc, sizeof(maple_t), "maple",
|
||||
(device_init_cb)&maple_init);
|
||||
mp->base.window =
|
||||
window_interface_create(NULL, (device_keydown_cb)&maple_keydown);
|
||||
|
||||
mp->devices[0] = maple_create_controller();
|
||||
mp->devices[0] = controller_create();
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
@ -44,6 +43,21 @@ void maple_destroy(maple_t *mp) {
|
|||
dc_destroy_device(&mp->base);
|
||||
}
|
||||
|
||||
void maple_vblank(maple_t *mp) {
|
||||
uint32_t enabled = mp->holly->reg[SB_MDEN];
|
||||
uint32_t vblank_initiate = mp->holly->reg[SB_MDTSEL];
|
||||
|
||||
// The controller can be started up by two methods: by software, or by
|
||||
// hardware
|
||||
// in synchronization with the V-BLANK signal. These methods are selected
|
||||
// through the trigger selection register (SB_MDTSEL).
|
||||
if (enabled && vblank_initiate) {
|
||||
maple_dma(mp);
|
||||
}
|
||||
|
||||
// TODO maple vblank interrupt?
|
||||
}
|
||||
|
||||
bool maple_init(maple_t *mp) {
|
||||
mp->holly = mp->base.dc->holly;
|
||||
mp->space = mp->base.dc->sh4->base.memory->space;
|
||||
|
@ -60,21 +74,6 @@ bool maple_init(maple_t *mp) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void maple_vblank(maple_t *mp) {
|
||||
uint32_t enabled = mp->holly->reg[SB_MDEN];
|
||||
uint32_t vblank_initiate = mp->holly->reg[SB_MDTSEL];
|
||||
|
||||
// The controller can be started up by two methods: by software, or by
|
||||
// hardware
|
||||
// in synchronization with the V-BLANK signal. These methods are selected
|
||||
// through the trigger selection register (SB_MDTSEL).
|
||||
if (enabled && vblank_initiate) {
|
||||
maple_dma(mp);
|
||||
}
|
||||
|
||||
// TODO maple vblank interrupt?
|
||||
}
|
||||
|
||||
void maple_dma(maple_t *mp) {
|
||||
uint32_t start_addr = mp->holly->reg[SB_MDSTAR];
|
||||
maple_transfer_t desc;
|
||||
|
|
|
@ -28,6 +28,8 @@ struct maple_s *maple_create(struct dreamcast_s *dc);
|
|||
void maple_destroy(struct maple_s *mp);
|
||||
void maple_vblank(struct maple_s *mp);
|
||||
|
||||
struct maple_device_s *controller_create();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include <imgui.h>
|
||||
#include "core/math.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/profiler.h"
|
||||
#include "core/string.h"
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/frontend/sh4/sh4_analyze.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/sh4/sh4_code_cache.h"
|
||||
#include "hw/dreamcast.h"
|
||||
|
@ -14,10 +16,6 @@
|
|||
#include "hw/holly/pvr.h"
|
||||
#include "hw/holly/ta.h"
|
||||
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::backend;
|
||||
using namespace re::jit::frontend::sh4;
|
||||
|
||||
static sh4_interrupt_info_t sh4_interrupts[NUM_SH_INTERRUPTS] = {
|
||||
#define SH4_INT(name, intevt, pri, ipr, ipr_shift) \
|
||||
{ intevt, pri, ipr, ipr_shift } \
|
||||
|
@ -177,18 +175,17 @@ bool sh4_init(sh4_t *sh4) {
|
|||
sh4->scheduler = sh4->base.dc->scheduler;
|
||||
sh4->space = sh4->base.memory->space;
|
||||
|
||||
re::jit::backend::MemoryInterface memif = {
|
||||
&sh4->ctx,
|
||||
sh4->base.memory->space->protected_base,
|
||||
sh4->base.memory->space,
|
||||
&address_space_r8,
|
||||
&address_space_r16,
|
||||
&address_space_r32,
|
||||
&address_space_r64,
|
||||
&address_space_w8,
|
||||
&address_space_w16,
|
||||
&address_space_w32,
|
||||
&address_space_w64};
|
||||
mem_interface_t memif = {&sh4->ctx,
|
||||
sh4->base.memory->space->protected_base,
|
||||
sh4->base.memory->space,
|
||||
&address_space_r8,
|
||||
&address_space_r16,
|
||||
&address_space_r32,
|
||||
&address_space_r64,
|
||||
&address_space_w8,
|
||||
&address_space_w16,
|
||||
&address_space_w32,
|
||||
&address_space_w64};
|
||||
sh4->code_cache = sh4_cache_create(&memif, &sh4_compile_pc);
|
||||
|
||||
// initialize context
|
||||
|
@ -252,7 +249,7 @@ void sh4_set_pc(sh4_t *sh4, uint32_t pc) {
|
|||
|
||||
static void sh4_run_inner(sh4_t *sh4, int64_t ns) {
|
||||
// execute at least 1 cycle. the tests rely on this to step block by block
|
||||
int64_t cycles = std::max(NANO_TO_CYCLES(ns, SH4_CLOCK_FREQ), INT64_C(1));
|
||||
int64_t cycles = MAX(NANO_TO_CYCLES(ns, SH4_CLOCK_FREQ), INT64_C(1));
|
||||
|
||||
// each block's epilog will decrement the remaining cycles as they run
|
||||
sh4->ctx.num_cycles = static_cast<int>(cycles);
|
||||
|
@ -399,11 +396,11 @@ void sh4_paint(sh4_t *sh4, bool show_main_menu) {
|
|||
|
||||
// calculate average mips
|
||||
float avg_mips = 0.0f;
|
||||
for (int i = std::max(0, perf->num_mips - MAX_MIPS_SAMPLES);
|
||||
i < perf->num_mips; i++) {
|
||||
for (int i = MAX(0, perf->num_mips - MAX_MIPS_SAMPLES); i < perf->num_mips;
|
||||
i++) {
|
||||
avg_mips += perf->mips[i % MAX_MIPS_SAMPLES];
|
||||
}
|
||||
avg_mips /= std::max(std::min(perf->num_mips, MAX_MIPS_SAMPLES), 1);
|
||||
avg_mips /= MAX(MIN(perf->num_mips, MAX_MIPS_SAMPLES), 1);
|
||||
|
||||
char overlay_text[128];
|
||||
snprintf(overlay_text, sizeof(overlay_text), "%.2f", avg_mips);
|
||||
|
@ -869,14 +866,14 @@ template <typename T>
|
|||
T sh4_read_cache(sh4_t *sh4, uint32_t addr) {
|
||||
CHECK_EQ(sh4->CCR->ORA, 1u);
|
||||
addr = CACHE_OFFSET(addr, sh4->CCR->OIX);
|
||||
return load<T>(&sh4->cache[addr]);
|
||||
return *(T *)&sh4->cache[addr];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void sh4_write_cache(sh4_t *sh4, uint32_t addr, T value) {
|
||||
CHECK_EQ(sh4->CCR->ORA, 1u);
|
||||
addr = CACHE_OFFSET(addr, sh4->CCR->OIX);
|
||||
store(&sh4->cache[addr], value);
|
||||
*(T *)&sh4->cache[addr] = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
#include "hw/sh4/sh4_code_cache.h"
|
||||
#include "hw/memory.h"
|
||||
#include "jit/backend/x64/x64_backend.h"
|
||||
#include "jit/frontend/sh4/sh4_analyze.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/frontend/frontend.h"
|
||||
#include "jit/ir/ir.h"
|
||||
// #include "jit/ir/passes/constant_propagation_pass.h"
|
||||
// #include "jit/ir/passes/conversion_elimination_pass.h"
|
||||
#include "jit/ir/passes/dead_code_elimination_pass.h"
|
||||
|
@ -12,14 +15,6 @@
|
|||
#include "jit/ir/passes/register_allocation_pass.h"
|
||||
#include "sys/filesystem.h"
|
||||
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::backend;
|
||||
using namespace re::jit::backend::x64;
|
||||
using namespace re::jit::frontend;
|
||||
using namespace re::jit::frontend::sh4;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
static bool sh4_cache_handle_exception(sh4_cache_t *cache, re_exception_t *ex);
|
||||
static sh4_block_t *sh4_cache_lookup_block(sh4_cache_t *cache,
|
||||
uint32_t guest_addr);
|
||||
|
@ -51,7 +46,7 @@ static rb_callback_t reverse_block_map_cb = {
|
|||
&reverse_block_map_cmp, NULL, NULL,
|
||||
};
|
||||
|
||||
sh4_cache_t *sh4_cache_create(const re::jit::backend::MemoryInterface *memif,
|
||||
sh4_cache_t *sh4_cache_create(const mem_interface_t *memif,
|
||||
code_pointer_t default_code) {
|
||||
sh4_cache_t *cache =
|
||||
reinterpret_cast<sh4_cache_t *>(calloc(1, sizeof(sh4_cache_t)));
|
||||
|
@ -62,21 +57,8 @@ sh4_cache_t *sh4_cache_create(const re::jit::backend::MemoryInterface *memif,
|
|||
cache, (exception_handler_cb)&sh4_cache_handle_exception);
|
||||
|
||||
// setup parser and emitter
|
||||
cache->frontend = new SH4Frontend();
|
||||
cache->backend = new X64Backend(*memif);
|
||||
|
||||
cache->pass_runner = new PassRunner();
|
||||
// setup optimization passes
|
||||
cache->pass_runner->AddPass(
|
||||
std::unique_ptr<Pass>(new LoadStoreEliminationPass()));
|
||||
// cache->pass_runner->AddPass(std::unique_ptr<Pass>(new
|
||||
// ConstantPropagationPass()));
|
||||
// cache->pass_runner->AddPass(std::unique_ptr<Pass>(new
|
||||
// ConversionEliminationPass()));
|
||||
cache->pass_runner->AddPass(
|
||||
std::unique_ptr<Pass>(new DeadCodeEliminationPass()));
|
||||
cache->pass_runner->AddPass(std::unique_ptr<Pass>(new RegisterAllocationPass(
|
||||
cache->backend->registers(), cache->backend->num_registers())));
|
||||
cache->frontend = sh4_frontend_create();
|
||||
cache->backend = x64_create(memif);
|
||||
|
||||
// initialize all entries in block cache to reference the default block
|
||||
cache->default_code = default_code;
|
||||
|
@ -90,9 +72,8 @@ sh4_cache_t *sh4_cache_create(const re::jit::backend::MemoryInterface *memif,
|
|||
|
||||
void sh4_cache_destroy(sh4_cache_t *cache) {
|
||||
exception_handler_remove(cache->eh_handle);
|
||||
delete cache->frontend;
|
||||
delete cache->backend;
|
||||
delete cache->pass_runner;
|
||||
sh4_frontend_destroy(cache->frontend);
|
||||
x64_destroy(cache->backend);
|
||||
free(cache);
|
||||
}
|
||||
|
||||
|
@ -123,9 +104,13 @@ static code_pointer_t sh4_cache_compile_code_inner(sh4_cache_t *cache,
|
|||
}
|
||||
|
||||
// translate the SH4 into IR
|
||||
ir_t ir = {};
|
||||
ir.buffer = cache->ir_buffer;
|
||||
ir.capacity = sizeof(cache->ir_buffer);
|
||||
|
||||
int guest_size = 0;
|
||||
IRBuilder &builder =
|
||||
cache->frontend->TranslateCode(guest_addr, guest_ptr, flags, &guest_size);
|
||||
cache->frontend->translate_code(cache->frontend, guest_addr, guest_ptr, flags,
|
||||
&guest_size, &ir);
|
||||
|
||||
#if 0
|
||||
const char *appdir = fs_appdir();
|
||||
|
@ -141,11 +126,15 @@ static code_pointer_t sh4_cache_compile_code_inner(sh4_cache_t *cache,
|
|||
builder.Dump(output);
|
||||
#endif
|
||||
|
||||
cache->pass_runner->Run(builder);
|
||||
// run optimization passes
|
||||
lse_run(&ir);
|
||||
dce_run(&ir);
|
||||
ra_run(&ir, cache->backend->registers, cache->backend->num_registers);
|
||||
|
||||
// assemble the IR into native code
|
||||
int host_size = 0;
|
||||
const uint8_t *host_addr = cache->backend->AssembleCode(builder, &host_size);
|
||||
const uint8_t *host_addr =
|
||||
cache->backend->assemble_code(cache->backend, &ir, &host_size);
|
||||
|
||||
if (!host_addr) {
|
||||
LOG_INFO("Assembler overflow, resetting block cache");
|
||||
|
@ -155,7 +144,7 @@ static code_pointer_t sh4_cache_compile_code_inner(sh4_cache_t *cache,
|
|||
|
||||
// if the backend fails to assemble on an empty cache, there's nothing to be
|
||||
// done
|
||||
host_addr = cache->backend->AssembleCode(builder, &host_size);
|
||||
host_addr = cache->backend->assemble_code(cache->backend, &ir, &host_size);
|
||||
|
||||
CHECK(host_addr, "Backend assembler buffer overflow");
|
||||
}
|
||||
|
@ -236,7 +225,7 @@ void sh4_cache_clear_blocks(sh4_cache_t *cache) {
|
|||
}
|
||||
|
||||
// have the backend reset its codegen buffers as well
|
||||
cache->backend->Reset();
|
||||
cache->backend->reset(cache->backend);
|
||||
}
|
||||
|
||||
static bool sh4_cache_handle_exception(sh4_cache_t *cache, re_exception_t *ex) {
|
||||
|
@ -249,7 +238,7 @@ static bool sh4_cache_handle_exception(sh4_cache_t *cache, re_exception_t *ex) {
|
|||
}
|
||||
|
||||
// let the backend attempt to handle the exception
|
||||
if (!cache->backend->HandleFastmemException(ex)) {
|
||||
if (!cache->backend->handle_fastmem_exception(cache->backend, ex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef SH4_CODE_CACHE_H
|
||||
#define SH4_CODE_CACHE_H
|
||||
|
||||
#include "core/assert.h"
|
||||
#include "core/rb_tree.h"
|
||||
#include "jit/backend/x64/x64_backend.h"
|
||||
#include "jit/frontend/sh4/sh4_context.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
#include "sys/exception_handler.h"
|
||||
|
||||
// executable code sits between 0x0c000000 and 0x0d000000 (16mb). each instr
|
||||
|
@ -15,6 +15,8 @@
|
|||
#define BLOCK_OFFSET(addr) ((addr & BLOCK_ADDR_MASK) >> BLOCK_ADDR_SHIFT)
|
||||
#define MAX_BLOCKS (0x1000000 >> BLOCK_ADDR_SHIFT)
|
||||
|
||||
struct jit_backend_s;
|
||||
struct mem_interface_s;
|
||||
struct sh4_block_s;
|
||||
|
||||
typedef uint32_t (*code_pointer_t)();
|
||||
|
@ -31,9 +33,10 @@ typedef struct sh4_block_s {
|
|||
|
||||
typedef struct sh4_cache_s {
|
||||
struct re_exception_handler_s *eh_handle;
|
||||
re::jit::frontend::Frontend *frontend;
|
||||
re::jit::backend::Backend *backend;
|
||||
re::jit::ir::passes::PassRunner *pass_runner;
|
||||
struct jit_frontend_s *frontend;
|
||||
struct jit_backend_s *backend;
|
||||
|
||||
uint8_t ir_buffer[1024 * 1024];
|
||||
|
||||
code_pointer_t default_code;
|
||||
code_pointer_t code[MAX_BLOCKS];
|
||||
|
@ -42,9 +45,8 @@ typedef struct sh4_cache_s {
|
|||
rb_tree_t reverse_blocks;
|
||||
} sh4_cache_t;
|
||||
|
||||
struct sh4_cache_s *sh4_cache_create(
|
||||
const re::jit::backend::MemoryInterface *memif,
|
||||
code_pointer_t default_code);
|
||||
struct sh4_cache_s *sh4_cache_create(const struct mem_interface_s *memif,
|
||||
code_pointer_t default_code);
|
||||
void sh4_cache_destroy(struct sh4_cache_s *cache);
|
||||
|
||||
static inline code_pointer_t sh4_cache_get_code(struct sh4_cache_s *cache,
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
#ifndef BACKEND_H
|
||||
#define BACKEND_H
|
||||
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct address_space_s;
|
||||
struct ir_s;
|
||||
struct re_exception_s;
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace backend {
|
||||
typedef struct register_def_s {
|
||||
const char *name;
|
||||
int value_types;
|
||||
const void *data;
|
||||
} register_def_t;
|
||||
|
||||
struct MemoryInterface {
|
||||
typedef struct mem_interface_s {
|
||||
void *ctx_base;
|
||||
void *mem_base;
|
||||
struct address_space_s *mem_self;
|
||||
|
@ -22,34 +29,31 @@ struct MemoryInterface {
|
|||
void (*w16)(struct address_space_s *, uint32_t, uint16_t);
|
||||
void (*w32)(struct address_space_s *, uint32_t, uint32_t);
|
||||
void (*w64)(struct address_space_s *, uint32_t, uint64_t);
|
||||
};
|
||||
} mem_interface_t;
|
||||
|
||||
struct Register {
|
||||
const char *name;
|
||||
int value_types;
|
||||
const void *data;
|
||||
};
|
||||
struct jit_backend_s;
|
||||
|
||||
class Backend {
|
||||
public:
|
||||
Backend(const MemoryInterface &memif) : memif_(memif) {}
|
||||
virtual ~Backend() {}
|
||||
typedef const register_def_t *(*registers_cb)();
|
||||
typedef int (*num_registers_cb)();
|
||||
typedef void (*reset_cb)(struct jit_backend_s *);
|
||||
typedef const uint8_t *(*assemble_code_cb)(struct jit_backend_s *,
|
||||
struct ir_s *, int *);
|
||||
typedef void (*dump_code_cb)(struct jit_backend_s *, const uint8_t *, int);
|
||||
typedef bool (*handle_fastmem_exception_cb)(struct jit_backend_s *,
|
||||
struct re_exception_s *);
|
||||
|
||||
virtual const Register *registers() const = 0;
|
||||
virtual int num_registers() const = 0;
|
||||
typedef struct jit_backend_s {
|
||||
const register_def_t *registers;
|
||||
int num_registers;
|
||||
|
||||
virtual void Reset() = 0;
|
||||
reset_cb reset;
|
||||
assemble_code_cb assemble_code;
|
||||
dump_code_cb dump_code;
|
||||
handle_fastmem_exception_cb handle_fastmem_exception;
|
||||
} jit_backend_t;
|
||||
|
||||
virtual const uint8_t *AssembleCode(ir::IRBuilder &builder, int *size) = 0;
|
||||
virtual void DumpCode(const uint8_t *host_addr, int size) = 0;
|
||||
|
||||
virtual bool HandleFastmemException(struct re_exception_s *ex) = 0;
|
||||
|
||||
protected:
|
||||
MemoryInterface memif_;
|
||||
};
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,51 +1,18 @@
|
|||
#ifndef X64_BACKEND_H
|
||||
#define X64_BACKEND_H
|
||||
|
||||
#include <capstone.h>
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/backend/x64/x64_emitter.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
struct jit_backend_s;
|
||||
struct mem_interface_s;
|
||||
|
||||
extern const Register x64_registers[];
|
||||
extern const int x64_num_registers;
|
||||
extern const int x64_arg0_idx;
|
||||
extern const int x64_arg1_idx;
|
||||
extern const int x64_arg2_idx;
|
||||
extern const int x64_tmp0_idx;
|
||||
extern const int x64_tmp1_idx;
|
||||
struct jit_backend_s *x64_create(const struct mem_interface_s *memif);
|
||||
void x64_destroy(struct jit_backend_s *b);
|
||||
|
||||
typedef void (*SlowmemThunk)();
|
||||
|
||||
class X64Backend : public Backend {
|
||||
public:
|
||||
X64Backend(const MemoryInterface &memif);
|
||||
~X64Backend();
|
||||
|
||||
const Register *registers() const;
|
||||
int num_registers() const;
|
||||
|
||||
void Reset();
|
||||
|
||||
const uint8_t *AssembleCode(ir::IRBuilder &builder, int *size);
|
||||
void DumpCode(const uint8_t *host_addr, int size);
|
||||
|
||||
bool HandleFastmemException(struct re_exception_s *ex);
|
||||
|
||||
private:
|
||||
void EmitThunks();
|
||||
|
||||
csh capstone_handle_;
|
||||
X64Emitter emitter_;
|
||||
SlowmemThunk load_thunk_[16];
|
||||
SlowmemThunk store_thunk_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
#include "core/memory.h"
|
||||
#include "jit/backend/x64/x64_disassembler.h"
|
||||
|
||||
using namespace re::jit::backend::x64;
|
||||
|
||||
bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
||||
bool x64_decode_mov(const uint8_t *data, x64_mov_t *mov) {
|
||||
const uint8_t *start = data;
|
||||
|
||||
// test for operand size prefix
|
||||
bool has_opprefix = false;
|
||||
|
||||
if (load<uint8_t>(data) == 0x66) {
|
||||
if (*data == 0x66) {
|
||||
has_opprefix = true;
|
||||
data++;
|
||||
}
|
||||
|
@ -22,8 +19,8 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
uint8_t rex_x = 0;
|
||||
uint8_t rex_b = 0;
|
||||
|
||||
if ((load<uint8_t>(data) & 0xf0) == 0x40) {
|
||||
rex = load<uint8_t>(data);
|
||||
if ((*data & 0xf0) == 0x40) {
|
||||
rex = *data;
|
||||
rex_w = rex & 0b1000;
|
||||
rex_r = rex & 0b0100;
|
||||
rex_x = rex & 0b0010;
|
||||
|
@ -41,40 +38,38 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
// MOV r16,r/m16
|
||||
// MOV r32,r/m32
|
||||
// MOV r64,r/m64
|
||||
if (load<uint8_t>(data) == 0x8a || load<uint8_t>(data) == 0x8b) {
|
||||
if (*data == 0x8a || *data == 0x8b) {
|
||||
is_load = true;
|
||||
has_imm = false;
|
||||
operand_size =
|
||||
load<uint8_t>(data) == 0x8a ? 1 : (has_opprefix ? 2 : (rex_w ? 8 : 4));
|
||||
operand_size = *data == 0x8a ? 1 : (has_opprefix ? 2 : (rex_w ? 8 : 4));
|
||||
data++;
|
||||
}
|
||||
// MOV r/m8,r8
|
||||
// MOV r/m16,r16
|
||||
// MOV r/m32,r32
|
||||
// MOV r/m64,r64
|
||||
else if (load<uint8_t>(data) == 0x88 || load<uint8_t>(data) == 0x89) {
|
||||
else if (*data == 0x88 || *data == 0x89) {
|
||||
is_load = false;
|
||||
has_imm = false;
|
||||
operand_size =
|
||||
load<uint8_t>(data) == 0x88 ? 1 : (has_opprefix ? 2 : (rex_w ? 8 : 4));
|
||||
operand_size = *data == 0x88 ? 1 : (has_opprefix ? 2 : (rex_w ? 8 : 4));
|
||||
data++;
|
||||
}
|
||||
// MOV r8,imm8
|
||||
// MOV r16,imm16
|
||||
// MOV r32,imm32
|
||||
else if (load<uint8_t>(data) == 0xb0 || load<uint8_t>(data) == 0xb8) {
|
||||
else if (*data == 0xb0 || *data == 0xb8) {
|
||||
is_load = true;
|
||||
has_imm = true;
|
||||
operand_size = load<uint8_t>(data) == 0xb0 ? 1 : (has_opprefix ? 2 : 4);
|
||||
operand_size = *data == 0xb0 ? 1 : (has_opprefix ? 2 : 4);
|
||||
data++;
|
||||
}
|
||||
// MOV r/m8,imm8
|
||||
// MOV r/m16,imm16
|
||||
// MOV r/m32,imm32
|
||||
else if (load<uint8_t>(data) == 0xc6 || load<uint8_t>(data) == 0xc7) {
|
||||
else if (*data == 0xc6 || *data == 0xc7) {
|
||||
is_load = false;
|
||||
has_imm = true;
|
||||
operand_size = load<uint8_t>(data) == 0xc6 ? 1 : (has_opprefix ? 2 : 4);
|
||||
operand_size = *data == 0xc6 ? 1 : (has_opprefix ? 2 : 4);
|
||||
data++;
|
||||
}
|
||||
// not a supported MOV instruction
|
||||
|
@ -83,7 +78,7 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
}
|
||||
|
||||
// process ModR/M byte
|
||||
uint8_t modrm = load<uint8_t>(data);
|
||||
uint8_t modrm = *data;
|
||||
uint8_t modrm_mod = (modrm & 0b11000000) >> 6;
|
||||
uint8_t modrm_reg = (modrm & 0b00111000) >> 3;
|
||||
uint8_t modrm_rm = (modrm & 0b00000111);
|
||||
|
@ -104,7 +99,7 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
|
||||
// process optional SIB byte
|
||||
if (modrm_rm == 0b100) {
|
||||
uint8_t sib = load<uint8_t>(data);
|
||||
uint8_t sib = *data;
|
||||
uint8_t sib_scale = (sib & 0b11000000) >> 6;
|
||||
uint8_t sib_index = (sib & 0b00111000) >> 3;
|
||||
uint8_t sib_base = (sib & 0b00000111);
|
||||
|
@ -125,18 +120,18 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
case 0b00: {
|
||||
// RIP-relative
|
||||
if (modrm_rm == 0b101) {
|
||||
mov->disp = load<uint32_t>(data);
|
||||
mov->disp = *(uint32_t *)data;
|
||||
data += 4;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 0b01: {
|
||||
mov->disp = load<uint8_t>(data);
|
||||
mov->disp = *data;
|
||||
data++;
|
||||
} break;
|
||||
|
||||
case 0b10: {
|
||||
mov->disp = load<uint32_t>(data);
|
||||
mov->disp = *(uint32_t *)data;
|
||||
data += 4;
|
||||
} break;
|
||||
}
|
||||
|
@ -145,29 +140,29 @@ bool X64Disassembler::DecodeMov(const uint8_t *data, X64Mov *mov) {
|
|||
if (mov->has_imm) {
|
||||
switch (mov->operand_size) {
|
||||
case 1: {
|
||||
mov->imm = load<uint8_t>(data);
|
||||
mov->imm = *data;
|
||||
data++;
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
mov->imm = load<uint16_t>(data);
|
||||
mov->imm = *(uint16_t *)data;
|
||||
data += 2;
|
||||
} break;
|
||||
|
||||
case 4: {
|
||||
mov->imm = load<uint32_t>(data);
|
||||
mov->imm = *(uint32_t *)data;
|
||||
data += 4;
|
||||
} break;
|
||||
|
||||
case 8: {
|
||||
mov->imm = load<uint64_t>(data);
|
||||
mov->imm = *(uint64_t *)data;
|
||||
data += 8;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate total instruction length
|
||||
mov->length = static_cast<int>(data - start);
|
||||
mov->length = (int)(data - start);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef X64_DISASSEMBLER_H
|
||||
#define X64_DISASSEMBLER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct X64Mov {
|
||||
typedef struct {
|
||||
int length;
|
||||
bool is_load;
|
||||
bool is_indirect;
|
||||
|
@ -22,15 +22,12 @@ struct X64Mov {
|
|||
int scale;
|
||||
int disp;
|
||||
uint64_t imm;
|
||||
};
|
||||
} x64_mov_t;
|
||||
|
||||
class X64Disassembler {
|
||||
public:
|
||||
static bool DecodeMov(const uint8_t *data, X64Mov *mov);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
bool x64_decode_mov(const uint8_t *data, x64_mov_t *mov);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,67 +0,0 @@
|
|||
#ifndef X64_EMITTER_H
|
||||
#define X64_EMITTER_H
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "jit/backend/backend.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
enum {
|
||||
#if PLATFORM_WINDOWS
|
||||
STACK_SHADOW_SPACE = 32,
|
||||
#else
|
||||
STACK_SHADOW_SPACE = 0,
|
||||
#endif
|
||||
STACK_OFFSET_LOCALS = STACK_SHADOW_SPACE,
|
||||
STACK_SIZE = STACK_OFFSET_LOCALS
|
||||
};
|
||||
|
||||
enum XmmConstant {
|
||||
XMM_CONST_ABS_MASK_PS,
|
||||
XMM_CONST_ABS_MASK_PD,
|
||||
XMM_CONST_SIGN_MASK_PS,
|
||||
XMM_CONST_SIGN_MASK_PD,
|
||||
NUM_XMM_CONST,
|
||||
};
|
||||
|
||||
class X64Emitter : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
X64Emitter(const MemoryInterface &memif, void *buffer, size_t buffer_size);
|
||||
~X64Emitter();
|
||||
|
||||
const MemoryInterface &memif() {
|
||||
return memif_;
|
||||
}
|
||||
|
||||
void Reset();
|
||||
|
||||
const uint8_t *Emit(ir::IRBuilder &builder, int *size);
|
||||
|
||||
// helpers for the emitter callbacks
|
||||
const Xbyak::Reg GetRegister(const ir::Value *v);
|
||||
const Xbyak::Xmm GetXmmRegister(const ir::Value *v);
|
||||
const Xbyak::Address GetXmmConstant(XmmConstant c);
|
||||
|
||||
bool CanEncodeAsImmediate(const ir::Value *v) const;
|
||||
|
||||
private:
|
||||
void EmitConstants();
|
||||
void EmitProlog(ir::IRBuilder &builder, int *stack_size);
|
||||
void EmitBody(ir::IRBuilder &builder);
|
||||
void EmitEpilog(ir::IRBuilder &builder, int stack_size);
|
||||
|
||||
MemoryInterface memif_;
|
||||
int modified_marker_;
|
||||
int *modified_;
|
||||
int num_temps_;
|
||||
Xbyak::Label xmm_const_[NUM_XMM_CONST];
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,22 +1,20 @@
|
|||
#ifndef FRONTEND_H
|
||||
#define FRONTEND_H
|
||||
|
||||
#include "jit/ir/ir_builder.h"
|
||||
struct ir_s;
|
||||
struct jit_frontend_s;
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace frontend {
|
||||
typedef void (*jit_frontend_translate_code)(struct jit_frontend_s *frontend,
|
||||
uint32_t guest_addr,
|
||||
uint8_t *guest_ptr, int flags,
|
||||
int *size, struct ir_s *ir);
|
||||
typedef void (*jit_frontend_dump_code)(struct jit_frontend_s *frontend,
|
||||
uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int size);
|
||||
|
||||
class Frontend {
|
||||
public:
|
||||
virtual ~Frontend() {}
|
||||
|
||||
virtual ir::IRBuilder &TranslateCode(uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int flags, int *size) = 0;
|
||||
virtual void DumpCode(uint32_t guest_addr, uint8_t *guest_ptr, int size) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
typedef struct jit_frontend_s {
|
||||
jit_frontend_translate_code translate_code;
|
||||
jit_frontend_dump_code dump_code;
|
||||
} jit_frontend_t;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,27 +1,22 @@
|
|||
#include "core/assert.h"
|
||||
#include "core/memory.h"
|
||||
#include "jit/frontend/sh4/sh4_analyzer.h"
|
||||
#include "jit/frontend/sh4/sh4_disassembler.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
#include "jit/frontend/sh4/sh4_analyze.h"
|
||||
#include "jit/frontend/sh4/sh4_disasm.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::jit::frontend::sh4;
|
||||
|
||||
void SH4Analyzer::AnalyzeBlock(uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int flags, int *size) {
|
||||
void sh4_analyze_block(uint32_t guest_addr, uint8_t *guest_ptr, int flags,
|
||||
int *size) {
|
||||
*size = 0;
|
||||
|
||||
while (true) {
|
||||
Instr instr;
|
||||
sh4_instr_t instr = {};
|
||||
instr.addr = guest_addr;
|
||||
instr.opcode = load<uint16_t>(guest_ptr);
|
||||
instr.opcode = *(uint16_t *)guest_ptr;
|
||||
|
||||
// end block on invalid instruction
|
||||
if (!SH4Disassembler::Disasm(&instr)) {
|
||||
if (!sh4_disasm(&instr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
int step = (instr.flags & OP_FLAG_DELAYED) ? 4 : 2;
|
||||
int step = (instr.flags & SH4_FLAG_DELAYED) ? 4 : 2;
|
||||
guest_addr += step;
|
||||
guest_ptr += step;
|
||||
*size += step;
|
||||
|
@ -30,7 +25,8 @@ void SH4Analyzer::AnalyzeBlock(uint32_t guest_addr, uint8_t *guest_ptr,
|
|||
// changed, stop emitting since the fpu state is invalidated. also, if
|
||||
// sr has changed, stop emitting as there are interrupts that possibly
|
||||
// need to be handled
|
||||
if (instr.flags & (OP_FLAG_BRANCH | OP_FLAG_SET_FPSCR | OP_FLAG_SET_SR)) {
|
||||
if (instr.flags &
|
||||
(SH4_FLAG_BRANCH | SH4_FLAG_SET_FPSCR | SH4_FLAG_SET_SR)) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef SH4_ANALYZER_H
|
||||
#define SH4_ANALYZER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
SH4_SLOWMEM = 0x1,
|
||||
SH4_DOUBLE_PR = 0x2,
|
||||
SH4_DOUBLE_SZ = 0x4,
|
||||
SH4_SINGLE_INSTR = 0x8,
|
||||
};
|
||||
|
||||
void sh4_analyze_block(uint32_t guest_addr, uint8_t *guest_ptr, int flags,
|
||||
int *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef SH4_ANALYZER_H
|
||||
#define SH4_ANALYZER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace frontend {
|
||||
namespace sh4 {
|
||||
|
||||
class SH4Analyzer {
|
||||
public:
|
||||
static void AnalyzeBlock(uint32_t guest_addr, uint8_t *guest_ptr, int flags,
|
||||
int *size);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,55 +0,0 @@
|
|||
#ifndef SH4_BUILDER_H
|
||||
#define SH4_BUILDER_H
|
||||
|
||||
#include "jit/frontend/sh4/sh4_context.h"
|
||||
#include "jit/frontend/sh4/sh4_disassembler.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace frontend {
|
||||
namespace sh4 {
|
||||
|
||||
class SH4Builder : public ir::IRBuilder {
|
||||
public:
|
||||
SH4Builder(Arena &arena);
|
||||
|
||||
int flags() {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void Emit(uint32_t guest_addr, uint8_t *guest_ptr, int size, int flags);
|
||||
|
||||
ir::Instr *LoadGuest(ir::Value *addr, ir::ValueType type);
|
||||
void StoreGuest(ir::Value *addr, ir::Value *v);
|
||||
ir::Value *LoadGPR(int n, ir::ValueType type);
|
||||
void StoreGPR(int n, ir::Value *v);
|
||||
ir::Value *LoadFPR(int n, ir::ValueType type);
|
||||
void StoreFPR(int n, ir::Value *v);
|
||||
ir::Value *LoadXFR(int n, ir::ValueType type);
|
||||
void StoreXFR(int n, ir::Value *v);
|
||||
ir::Value *LoadSR();
|
||||
void StoreSR(ir::Value *v);
|
||||
ir::Value *LoadT();
|
||||
void StoreT(ir::Value *v);
|
||||
ir::Value *LoadGBR();
|
||||
void StoreGBR(ir::Value *v);
|
||||
ir::Value *LoadFPSCR();
|
||||
void StoreFPSCR(ir::Value *v);
|
||||
ir::Value *LoadPR();
|
||||
void StorePR(ir::Value *v);
|
||||
|
||||
void InvalidInstruction(uint32_t guest_addr);
|
||||
void EmitDelayInstr();
|
||||
|
||||
private:
|
||||
Instr delay_instr_;
|
||||
int flags_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,13 +1,9 @@
|
|||
#include "core/assert.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/string.h"
|
||||
#include "jit/frontend/sh4/sh4_disassembler.h"
|
||||
#include "jit/frontend/sh4/sh4_disasm.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::jit::frontend::sh4;
|
||||
|
||||
struct InstrType {
|
||||
Op op;
|
||||
typedef struct {
|
||||
sh4_op_t op;
|
||||
const char *desc;
|
||||
const char *sig;
|
||||
int cycles;
|
||||
|
@ -17,20 +13,20 @@ struct InstrType {
|
|||
uint16_t disp_mask, disp_shift;
|
||||
uint16_t rm_mask, rm_shift;
|
||||
uint16_t rn_mask, rn_shift;
|
||||
};
|
||||
} sh4_opdef_t;
|
||||
|
||||
static InstrType s_instrs[NUM_OPCODES] = {
|
||||
{OP_INVALID, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
#define SH4_INSTR(name, desc, sig, cycles, flags) \
|
||||
{ OP_##name, desc, #sig, cycles, flags, 0, 0, 0, 0, 0, 0, 0, 0, 0 } \
|
||||
static sh4_opdef_t s_opdefs[NUM_SH4_OPS] = {
|
||||
{SH4_OP_INVALID, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
#define SH4_INSTR(name, desc, sig, cycles, flags) \
|
||||
{ SH4_OP_##name, desc, #sig, cycles, flags, 0, 0, 0, 0, 0, 0, 0, 0, 0 } \
|
||||
,
|
||||
#include "jit/frontend/sh4/sh4_instr.inc"
|
||||
#undef SH4_INSTR
|
||||
};
|
||||
static InstrType *s_instr_lookup[UINT16_MAX] = {};
|
||||
static sh4_opdef_t *s_opdef_lookup[UINT16_MAX] = {};
|
||||
|
||||
static void GetArgMask(const char *instr_code, char c, uint16_t *mask,
|
||||
uint16_t *shift) {
|
||||
static void sh4_arg_mask(const char *instr_code, char c, uint16_t *mask,
|
||||
uint16_t *shift) {
|
||||
size_t len = strlen(instr_code);
|
||||
if (mask)
|
||||
*mask = 0;
|
||||
|
@ -41,12 +37,12 @@ static void GetArgMask(const char *instr_code, char c, uint16_t *mask,
|
|||
if (mask)
|
||||
*mask |= (1 << (len - i - 1));
|
||||
if (shift)
|
||||
*shift = static_cast<uint16_t>(len - i - 1);
|
||||
*shift = (uint16_t)(len - i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InitInstrTables() {
|
||||
static void sh4_init_opdefs() {
|
||||
static bool initialized = false;
|
||||
|
||||
if (initialized) {
|
||||
|
@ -57,14 +53,14 @@ static void InitInstrTables() {
|
|||
|
||||
// finalize type information by extracting argument encoding information
|
||||
// from signatures
|
||||
for (int i = 1 /* skip OP_INVALID */; i < NUM_OPCODES; i++) {
|
||||
InstrType *type = &s_instrs[i];
|
||||
for (int i = 1 /* skip SH4_OP_INVALID */; i < NUM_SH4_OPS; i++) {
|
||||
sh4_opdef_t *def = &s_opdefs[i];
|
||||
|
||||
GetArgMask(type->sig, 'i', &type->imm_mask, &type->imm_shift);
|
||||
GetArgMask(type->sig, 'd', &type->disp_mask, &type->disp_shift);
|
||||
GetArgMask(type->sig, 'm', &type->rm_mask, &type->rm_shift);
|
||||
GetArgMask(type->sig, 'n', &type->rn_mask, &type->rn_shift);
|
||||
GetArgMask(type->sig, 0, &type->opcode_mask, NULL);
|
||||
sh4_arg_mask(def->sig, 'i', &def->imm_mask, &def->imm_shift);
|
||||
sh4_arg_mask(def->sig, 'd', &def->disp_mask, &def->disp_shift);
|
||||
sh4_arg_mask(def->sig, 'm', &def->rm_mask, &def->rm_shift);
|
||||
sh4_arg_mask(def->sig, 'n', &def->rn_mask, &def->rn_shift);
|
||||
sh4_arg_mask(def->sig, 0, &def->opcode_mask, NULL);
|
||||
}
|
||||
|
||||
// initialize lookup table
|
||||
|
@ -74,13 +70,13 @@ static void InitInstrTables() {
|
|||
for (int z = 0; z < 0x10; z++) {
|
||||
uint16_t value = w + x + y + z;
|
||||
|
||||
for (int i = 1 /* skip OP_INVALID */; i < NUM_OPCODES; i++) {
|
||||
InstrType *type = &s_instrs[i];
|
||||
uint16_t arg_mask = type->imm_mask | type->disp_mask |
|
||||
type->rm_mask | type->rn_mask;
|
||||
for (int i = 1 /* skip SH4_OP_INVALID */; i < NUM_SH4_OPS; i++) {
|
||||
sh4_opdef_t *def = &s_opdefs[i];
|
||||
uint16_t arg_mask =
|
||||
def->imm_mask | def->disp_mask | def->rm_mask | def->rn_mask;
|
||||
|
||||
if ((value & ~arg_mask) == type->opcode_mask) {
|
||||
s_instr_lookup[value] = type;
|
||||
if ((value & ~arg_mask) == def->opcode_mask) {
|
||||
s_opdef_lookup[value] = def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -90,30 +86,32 @@ static void InitInstrTables() {
|
|||
}
|
||||
}
|
||||
|
||||
bool SH4Disassembler::Disasm(Instr *i) {
|
||||
InitInstrTables();
|
||||
bool sh4_disasm(sh4_instr_t *i) {
|
||||
sh4_init_opdefs();
|
||||
|
||||
InstrType *type = s_instr_lookup[i->opcode];
|
||||
sh4_opdef_t *def = s_opdef_lookup[i->opcode];
|
||||
|
||||
if (!type) {
|
||||
i->op = OP_INVALID;
|
||||
if (!def) {
|
||||
i->op = SH4_OP_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
i->op = type->op;
|
||||
i->cycles = type->cycles;
|
||||
i->flags = type->flags;
|
||||
i->Rm = (i->opcode & type->rm_mask) >> type->rm_shift;
|
||||
i->Rn = (i->opcode & type->rn_mask) >> type->rn_shift;
|
||||
i->disp = (i->opcode & type->disp_mask) >> type->disp_shift;
|
||||
i->imm = (i->opcode & type->imm_mask) >> type->imm_shift;
|
||||
i->op = def->op;
|
||||
i->cycles = def->cycles;
|
||||
i->flags = def->flags;
|
||||
i->Rm = (i->opcode & def->rm_mask) >> def->rm_shift;
|
||||
i->Rn = (i->opcode & def->rn_mask) >> def->rn_shift;
|
||||
i->disp = (i->opcode & def->disp_mask) >> def->disp_shift;
|
||||
i->imm = (i->opcode & def->imm_mask) >> def->imm_shift;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SH4Disassembler::Format(const Instr &i, char *buffer, size_t buffer_size) {
|
||||
if (i.op == OP_INVALID) {
|
||||
snprintf(buffer, buffer_size, "%08x .word 0x%04x", i.addr, i.opcode);
|
||||
void sh4_format(const sh4_instr_t *i, char *buffer, size_t buffer_size) {
|
||||
sh4_init_opdefs();
|
||||
|
||||
if (i->op == SH4_OP_INVALID) {
|
||||
snprintf(buffer, buffer_size, "%08x .word 0x%04x", i->addr, i->opcode);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -123,7 +121,7 @@ void SH4Disassembler::Format(const Instr &i, char *buffer, size_t buffer_size) {
|
|||
uint32_t pcmask;
|
||||
|
||||
// copy initial formatted description
|
||||
snprintf(buffer, buffer_size, "%08x %s", i.addr, s_instrs[i.op].desc);
|
||||
snprintf(buffer, buffer_size, "%08x %s", i->addr, s_opdefs[i->op].desc);
|
||||
|
||||
// used by mov operators with displacements
|
||||
if (strnstr(buffer, ".b", buffer_size)) {
|
||||
|
@ -141,70 +139,70 @@ void SH4Disassembler::Format(const Instr &i, char *buffer, size_t buffer_size) {
|
|||
}
|
||||
|
||||
// (disp:4,rn)
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,rn)", i.disp * movsize);
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,rn)", i->disp * movsize);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "(disp:4,rn)", 11, value, value_len),
|
||||
0);
|
||||
|
||||
// (disp:4,rm)
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,rm)", i.disp * movsize);
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,rm)", i->disp * movsize);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "(disp:4,rm)", 11, value, value_len),
|
||||
0);
|
||||
|
||||
// (disp:8,gbr)
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,gbr)", i.disp * movsize);
|
||||
value_len = snprintf(value, sizeof(value), "(0x%x,gbr)", i->disp * movsize);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "(disp:8,gbr)", 12, value, value_len),
|
||||
0);
|
||||
|
||||
// (disp:8,pc)
|
||||
value_len = snprintf(value, sizeof(value), "(0x%08x)",
|
||||
(i.disp * movsize) + (i.addr & pcmask) + 4);
|
||||
(i->disp * movsize) + (i->addr & pcmask) + 4);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "(disp:8,pc)", 11, value, value_len),
|
||||
0);
|
||||
|
||||
// disp:8
|
||||
value_len = snprintf(value, sizeof(value), "0x%08x",
|
||||
((int8_t)i.disp * 2) + i.addr + 4);
|
||||
((int8_t)i->disp * 2) + i->addr + 4);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "disp:8", 6, value, value_len), 0);
|
||||
|
||||
// disp:12
|
||||
value_len =
|
||||
snprintf(value, sizeof(value), "0x%08x",
|
||||
((((int32_t)(i.disp & 0xfff) << 20) >> 20) * 2) + i.addr + 4);
|
||||
((((int32_t)(i->disp & 0xfff) << 20) >> 20) * 2) + i->addr + 4);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "disp:12", 7, value, value_len), 0);
|
||||
|
||||
// drm
|
||||
value_len = snprintf(value, sizeof(value), "dr%d", i.Rm);
|
||||
value_len = snprintf(value, sizeof(value), "dr%d", i->Rm);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "drm", 3, value, value_len), 0);
|
||||
|
||||
// drn
|
||||
value_len = snprintf(value, sizeof(value), "dr%d", i.Rn);
|
||||
value_len = snprintf(value, sizeof(value), "dr%d", i->Rn);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "drn", 3, value, value_len), 0);
|
||||
|
||||
// frm
|
||||
value_len = snprintf(value, sizeof(value), "fr%d", i.Rm);
|
||||
value_len = snprintf(value, sizeof(value), "fr%d", i->Rm);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "frm", 3, value, value_len), 0);
|
||||
|
||||
// frn
|
||||
value_len = snprintf(value, sizeof(value), "fr%d", i.Rn);
|
||||
value_len = snprintf(value, sizeof(value), "fr%d", i->Rn);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "frn", 3, value, value_len), 0);
|
||||
|
||||
// fvm
|
||||
value_len = snprintf(value, sizeof(value), "fv%d", i.Rm);
|
||||
value_len = snprintf(value, sizeof(value), "fv%d", i->Rm);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "fvm", 3, value, value_len), 0);
|
||||
|
||||
// fvn
|
||||
value_len = snprintf(value, sizeof(value), "fv%d", i.Rn);
|
||||
value_len = snprintf(value, sizeof(value), "fv%d", i->Rn);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "fvn", 3, value, value_len), 0);
|
||||
|
||||
// rm
|
||||
value_len = snprintf(value, sizeof(value), "r%d", i.Rm);
|
||||
value_len = snprintf(value, sizeof(value), "r%d", i->Rm);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "rm", 2, value, value_len), 0);
|
||||
|
||||
// rn
|
||||
value_len = snprintf(value, sizeof(value), "r%d", i.Rn);
|
||||
value_len = snprintf(value, sizeof(value), "r%d", i->Rn);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "rn", 2, value, value_len), 0);
|
||||
|
||||
// #imm8
|
||||
value_len = snprintf(value, sizeof(value), "0x%02x", i.imm);
|
||||
value_len = snprintf(value, sizeof(value), "0x%02x", i->imm);
|
||||
CHECK_EQ(strnrep(buffer, buffer_size, "#imm8", 5, value, value_len), 0);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef SH4_DISASSEMBLER_H
|
||||
#define SH4_DISASSEMBLER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SH4_OP_INVALID,
|
||||
#define SH4_INSTR(name, desc, instr_code, cycles, flags) SH4_OP_##name,
|
||||
#include "jit/frontend/sh4/sh4_instr.inc"
|
||||
#undef SH4_INSTR
|
||||
NUM_SH4_OPS,
|
||||
} sh4_op_t;
|
||||
|
||||
typedef enum {
|
||||
SH4_FLAG_BRANCH = 0x1,
|
||||
SH4_FLAG_CONDITIONAL = 0x2,
|
||||
SH4_FLAG_DELAYED = 0x4,
|
||||
SH4_FLAG_SET_T = 0x8,
|
||||
SH4_FLAG_SET_FPSCR = 0x10,
|
||||
SH4_FLAG_SET_SR = 0x20,
|
||||
} sh4_flag_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t addr;
|
||||
uint16_t opcode;
|
||||
|
||||
sh4_op_t op;
|
||||
int cycles;
|
||||
int flags;
|
||||
uint16_t Rm;
|
||||
uint16_t Rn;
|
||||
uint16_t disp;
|
||||
uint16_t imm;
|
||||
} sh4_instr_t;
|
||||
|
||||
bool sh4_disasm(sh4_instr_t *i);
|
||||
void sh4_format(const sh4_instr_t *i, char *buffer, size_t buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef SH4_DISASSEMBLER_H
|
||||
#define SH4_DISASSEMBLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace frontend {
|
||||
namespace sh4 {
|
||||
|
||||
enum Op {
|
||||
OP_INVALID,
|
||||
#define SH4_INSTR(name, desc, instr_code, cycles, flags) OP_##name,
|
||||
#include "jit/frontend/sh4/sh4_instr.inc"
|
||||
#undef SH4_INSTR
|
||||
NUM_OPCODES,
|
||||
};
|
||||
|
||||
enum OpFlag {
|
||||
OP_FLAG_BRANCH = 0x1,
|
||||
OP_FLAG_CONDITIONAL = 0x2,
|
||||
OP_FLAG_DELAYED = 0x4,
|
||||
OP_FLAG_SET_T = 0x8,
|
||||
OP_FLAG_SET_FPSCR = 0x10,
|
||||
OP_FLAG_SET_SR = 0x20,
|
||||
};
|
||||
|
||||
struct Instr {
|
||||
uint32_t addr;
|
||||
uint16_t opcode;
|
||||
|
||||
Op op;
|
||||
int cycles;
|
||||
int flags;
|
||||
uint16_t Rm;
|
||||
uint16_t Rn;
|
||||
uint16_t disp;
|
||||
uint16_t imm;
|
||||
};
|
||||
|
||||
class SH4Disassembler {
|
||||
public:
|
||||
static bool Disasm(Instr *i);
|
||||
static void Format(const Instr &i, char *buffer, size_t buffer_size);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
#include "jit/frontend/sh4/sh4_analyze.h"
|
||||
#include "jit/frontend/sh4/sh4_disasm.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
#include "jit/frontend/sh4/sh4_translate.h"
|
||||
#include "jit/frontend/frontend.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
typedef struct sh4_frontend_s { jit_frontend_t base; } sh4_frontend_t;
|
||||
|
||||
static void sh4_frontend_translate_code(sh4_frontend_t *frontend,
|
||||
uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int flags, int *size, ir_t *ir) {
|
||||
// get the block size
|
||||
sh4_analyze_block(guest_addr, guest_ptr, flags, size);
|
||||
|
||||
// emit IR for the SH4 code
|
||||
sh4_translate(guest_addr, guest_ptr, *size, flags, ir);
|
||||
}
|
||||
|
||||
static void sh4_frontend_dump_code(sh4_frontend_t *frontend,
|
||||
uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int size) {
|
||||
char buffer[128];
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (i < size) {
|
||||
sh4_instr_t instr = {};
|
||||
instr.addr = guest_addr + i;
|
||||
instr.opcode = *(uint16_t *)(guest_ptr + i);
|
||||
sh4_disasm(&instr);
|
||||
|
||||
sh4_format(&instr, buffer, sizeof(buffer));
|
||||
LOG_INFO(buffer);
|
||||
|
||||
i += 2;
|
||||
|
||||
if (instr.flags & SH4_FLAG_DELAYED) {
|
||||
sh4_instr_t delay = {};
|
||||
delay.addr = guest_addr + i;
|
||||
delay.opcode = *(uint16_t *)(guest_ptr + i);
|
||||
sh4_disasm(&delay);
|
||||
|
||||
sh4_format(&delay, buffer, sizeof(buffer));
|
||||
LOG_INFO(buffer);
|
||||
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jit_frontend_t *sh4_frontend_create() {
|
||||
sh4_frontend_t *frontend = calloc(1, sizeof(sh4_frontend_t));
|
||||
|
||||
frontend->base.translate_code =
|
||||
(jit_frontend_translate_code)&sh4_frontend_translate_code;
|
||||
frontend->base.dump_code = (jit_frontend_dump_code)&sh4_frontend_dump_code;
|
||||
|
||||
return (jit_frontend_t *)frontend;
|
||||
}
|
||||
|
||||
void sh4_frontend_destroy(jit_frontend_t *jit_frontend) {
|
||||
sh4_frontend_t *frontend = (sh4_frontend_t *)jit_frontend;
|
||||
|
||||
free(frontend);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
#include "core/memory.h"
|
||||
#include "jit/frontend/sh4/sh4_analyzer.h"
|
||||
#include "jit/frontend/sh4/sh4_builder.h"
|
||||
#include "jit/frontend/sh4/sh4_frontend.h"
|
||||
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::frontend::sh4;
|
||||
using namespace re::jit::ir;
|
||||
|
||||
SH4Frontend::SH4Frontend() : arena_(4096) {}
|
||||
|
||||
IRBuilder &SH4Frontend::TranslateCode(uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int flags, int *size) {
|
||||
// get the block size
|
||||
SH4Analyzer::AnalyzeBlock(guest_addr, guest_ptr, flags, size);
|
||||
|
||||
// emit IR for the SH4 code
|
||||
arena_.Reset();
|
||||
SH4Builder *builder = arena_.Alloc<SH4Builder>();
|
||||
new (builder) SH4Builder(arena_);
|
||||
builder->Emit(guest_addr, guest_ptr, *size, flags);
|
||||
|
||||
return *builder;
|
||||
}
|
||||
|
||||
void SH4Frontend::DumpCode(uint32_t guest_addr, uint8_t *guest_ptr, int size) {
|
||||
char buffer[128];
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (i < size) {
|
||||
Instr instr;
|
||||
instr.addr = guest_addr + i;
|
||||
instr.opcode = load<uint16_t>(guest_ptr + i);
|
||||
SH4Disassembler::Disasm(&instr);
|
||||
|
||||
SH4Disassembler::Format(instr, buffer, sizeof(buffer));
|
||||
LOG_INFO(buffer);
|
||||
|
||||
i += 2;
|
||||
|
||||
if (instr.flags & OP_FLAG_DELAYED) {
|
||||
Instr delay;
|
||||
delay.addr = guest_addr + i;
|
||||
delay.opcode = load<uint16_t>(guest_ptr + i);
|
||||
SH4Disassembler::Disasm(&delay);
|
||||
|
||||
SH4Disassembler::Format(delay, buffer, sizeof(buffer));
|
||||
LOG_INFO(buffer);
|
||||
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,17 @@
|
|||
#ifndef SH4_FRONTEND_H
|
||||
#define SH4_FRONTEND_H
|
||||
|
||||
#include "core/arena.h"
|
||||
#include "jit/frontend/frontend.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace frontend {
|
||||
namespace sh4 {
|
||||
struct jit_frontend_s;
|
||||
|
||||
enum SH4BlockFlags {
|
||||
SH4_SLOWMEM = 0x1,
|
||||
SH4_DOUBLE_PR = 0x2,
|
||||
SH4_DOUBLE_SZ = 0x4,
|
||||
SH4_SINGLE_INSTR = 0x8,
|
||||
};
|
||||
struct jit_frontend_s *sh4_frontend_create();
|
||||
void sh4_frontend_destroy(struct jit_frontend_s *frontend);
|
||||
|
||||
class SH4Frontend : public Frontend {
|
||||
public:
|
||||
SH4Frontend();
|
||||
|
||||
ir::IRBuilder &TranslateCode(uint32_t guest_addr, uint8_t *guest_ptr,
|
||||
int flags, int *size);
|
||||
void DumpCode(uint32_t guest_addr, uint8_t *guest_ptr, int size);
|
||||
|
||||
private:
|
||||
Arena arena_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,23 +47,23 @@ SH4_INSTR(XTRCT, "xtrct rm, rn", 0010nnnnmmmm1101, 1, 0)
|
|||
// arithmetric operation instructions
|
||||
SH4_INSTR(ADD, "add rm, rn", 0011nnnnmmmm1100, 1, 0)
|
||||
SH4_INSTR(ADDI, "add #imm8, rn", 0111nnnniiiiiiii, 1, 0)
|
||||
SH4_INSTR(ADDC, "addc rm, rn", 0011nnnnmmmm1110, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ADDV, "addv rm, rn", 0011nnnnmmmm1111, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPEQI, "cmp/eq #imm8, r0", 10001000iiiiiiii, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPEQ, "cmp/eq rm, rn", 0011nnnnmmmm0000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPHS, "cmp/hs rm, rn", 0011nnnnmmmm0010, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPGE, "cmp/ge rm, rn", 0011nnnnmmmm0011, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPHI, "cmp/hi rm, rn", 0011nnnnmmmm0110, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPGT, "cmp/gt rm, rn", 0011nnnnmmmm0111, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPPZ, "cmp/pz rn", 0100nnnn00010001, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPPL, "cmp/pl rn", 0100nnnn00010101, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(CMPSTR, "cmp/str rm, rn", 0010nnnnmmmm1100, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(DIV0S, "div0s rm, rn", 0010nnnnmmmm0111, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(DIV0U, "div0u", 0000000000011001, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(DIV1, "div1 rm, rn", 0011nnnnmmmm0100, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ADDC, "addc rm, rn", 0011nnnnmmmm1110, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(ADDV, "addv rm, rn", 0011nnnnmmmm1111, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPEQI, "cmp/eq #imm8, r0", 10001000iiiiiiii, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPEQ, "cmp/eq rm, rn", 0011nnnnmmmm0000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPHS, "cmp/hs rm, rn", 0011nnnnmmmm0010, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPGE, "cmp/ge rm, rn", 0011nnnnmmmm0011, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPHI, "cmp/hi rm, rn", 0011nnnnmmmm0110, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPGT, "cmp/gt rm, rn", 0011nnnnmmmm0111, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPPZ, "cmp/pz rn", 0100nnnn00010001, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPPL, "cmp/pl rn", 0100nnnn00010101, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(CMPSTR, "cmp/str rm, rn", 0010nnnnmmmm1100, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(DIV0S, "div0s rm, rn", 0010nnnnmmmm0111, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(DIV0U, "div0u", 0000000000011001, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(DIV1, "div1 rm, rn", 0011nnnnmmmm0100, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(DMULS, "dmuls.l rm, rn", 0011nnnnmmmm1101, 2, 0)
|
||||
SH4_INSTR(DMULU, "dmulu.l rm, rn", 0011nnnnmmmm0101, 2, 0)
|
||||
SH4_INSTR(DT, "dt rn", 0100nnnn00010000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(DT, "dt rn", 0100nnnn00010000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(EXTSB, "exts.b rm, rn", 0110nnnnmmmm1110, 1, 0)
|
||||
SH4_INSTR(EXTSW, "exts.w rm, rn", 0110nnnnmmmm1111, 1, 0)
|
||||
SH4_INSTR(EXTUB, "extu.b rm, rn", 0110nnnnmmmm1100, 1, 0)
|
||||
|
@ -74,10 +74,10 @@ SH4_INSTR(MULL, "mul.l rm, rn", 0000nnnnmmmm0111, 2, 0)
|
|||
SH4_INSTR(MULS, "muls rm, rn", 0010nnnnmmmm1111, 2, 0)
|
||||
SH4_INSTR(MULU, "mulu rm, rn", 0010nnnnmmmm1110, 2, 0)
|
||||
SH4_INSTR(NEG, "neg rm, rn", 0110nnnnmmmm1011, 1, 0)
|
||||
SH4_INSTR(NEGC, "negc rm, rn", 0110nnnnmmmm1010, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(NEGC, "negc rm, rn", 0110nnnnmmmm1010, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SUB, "sub rm, rn", 0011nnnnmmmm1000, 1, 0)
|
||||
SH4_INSTR(SUBC, "subc rm, rn", 0011nnnnmmmm1010, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SUBV, "subv rm, rn", 0011nnnnmmmm1011, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SUBC, "subc rm, rn", 0011nnnnmmmm1010, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SUBV, "subv rm, rn", 0011nnnnmmmm1011, 1, SH4_FLAG_SET_T)
|
||||
|
||||
|
||||
// logic operation instructions
|
||||
|
@ -88,26 +88,26 @@ SH4_INSTR(NOT, "not rm, rn", 0110nnnnmmmm0111, 1, 0)
|
|||
SH4_INSTR(OR, "or rm, rn", 0010nnnnmmmm1011, 1, 0)
|
||||
SH4_INSTR(ORI, "or #imm8, r0", 11001011iiiiiiii, 1, 0)
|
||||
SH4_INSTR(ORB, "or.b #imm8, @(r0,gbr)", 11001111iiiiiiii, 4, 0)
|
||||
SH4_INSTR(TAS, "tas.b @rn", 0100nnnn00011011, 5, OP_FLAG_SET_T)
|
||||
SH4_INSTR(TST, "tst rm, rn", 0010nnnnmmmm1000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(TSTI, "tst #imm8, r0", 11001000iiiiiiii, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(TSTB, "tst.b #imm8, @(r0,gbr)", 11001100iiiiiiii, 3, OP_FLAG_SET_T)
|
||||
SH4_INSTR(TAS, "tas.b @rn", 0100nnnn00011011, 5, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(TST, "tst rm, rn", 0010nnnnmmmm1000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(TSTI, "tst #imm8, r0", 11001000iiiiiiii, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(TSTB, "tst.b #imm8, @(r0,gbr)", 11001100iiiiiiii, 3, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(XOR, "xor rm, rn", 0010nnnnmmmm1010, 1, 0)
|
||||
SH4_INSTR(XORI, "xor #imm8, r0", 11001010iiiiiiii, 1, 0)
|
||||
SH4_INSTR(XORB, "xor.b #imm8, @(r0,gbr)", 11001110iiiiiiii, 4, 0)
|
||||
|
||||
|
||||
// shift instructions
|
||||
SH4_INSTR(ROTL, "rotl rn", 0100nnnn00000100, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ROTR, "rotr rn", 0100nnnn00000101, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ROTCL, "rotcl rn", 0100nnnn00100100, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ROTCR, "rotcr rn", 0100nnnn00100101, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(ROTL, "rotl rn", 0100nnnn00000100, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(ROTR, "rotr rn", 0100nnnn00000101, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(ROTCL, "rotcl rn", 0100nnnn00100100, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(ROTCR, "rotcr rn", 0100nnnn00100101, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SHAD, "shad rm, rn", 0100nnnnmmmm1100, 1, 0)
|
||||
SH4_INSTR(SHAL, "shal rn", 0100nnnn00100000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SHAR, "shar rn", 0100nnnn00100001, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SHAL, "shal rn", 0100nnnn00100000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SHAR, "shar rn", 0100nnnn00100001, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SHLD, "shld rm, rn", 0100nnnnmmmm1101, 1, 0)
|
||||
SH4_INSTR(SHLL, "shll rn", 0100nnnn00000000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SHLR, "shlr rn", 0100nnnn00000001, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SHLL, "shll rn", 0100nnnn00000000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SHLR, "shlr rn", 0100nnnn00000001, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SHLL2, "shll2 rn", 0100nnnn00001000, 1, 0)
|
||||
SH4_INSTR(SHLR2, "shlr2 rn", 0100nnnn00001001, 1, 0)
|
||||
SH4_INSTR(SHLL8, "shll8 rn", 0100nnnn00011000, 1, 0)
|
||||
|
@ -119,31 +119,31 @@ SH4_INSTR(SHLR16, "shlr16 rn", 0100nnnn00101001, 1, 0)
|
|||
// branch instructions
|
||||
// can we sign extend bdisp12 in sh4_instr code, not inside of sh4_builder
|
||||
// then, we can reuse some more of these disp* types
|
||||
SH4_INSTR(BF, "bf disp:8", 10001011dddddddd, 1, OP_FLAG_CONDITIONAL | OP_FLAG_BRANCH)
|
||||
SH4_INSTR(BFS, "bfs disp:8", 10001111dddddddd, 1, OP_FLAG_CONDITIONAL | OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BT, "bt disp:8", 10001001dddddddd, 1, OP_FLAG_CONDITIONAL | OP_FLAG_BRANCH)
|
||||
SH4_INSTR(BTS, "bts disp:8", 10001101dddddddd, 1, OP_FLAG_CONDITIONAL | OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BRA, "bra disp:12", 1010dddddddddddd, 1, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BRAF, "braf rn", 0000nnnn00100011, 2, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BSR, "bsr disp:12", 1011dddddddddddd, 1, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BSRF, "bsrf rn", 0000nnnn00000011, 2, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(JMP, "jmp @rm", 0100nnnn00101011, 2, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(JSR, "jsr @rn", 0100nnnn00001011, 2, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(RTS, "rts", 0000000000001011, 2, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(BF, "bf disp:8", 10001011dddddddd, 1, SH4_FLAG_CONDITIONAL | SH4_FLAG_BRANCH)
|
||||
SH4_INSTR(BFS, "bfs disp:8", 10001111dddddddd, 1, SH4_FLAG_CONDITIONAL | SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(BT, "bt disp:8", 10001001dddddddd, 1, SH4_FLAG_CONDITIONAL | SH4_FLAG_BRANCH)
|
||||
SH4_INSTR(BTS, "bts disp:8", 10001101dddddddd, 1, SH4_FLAG_CONDITIONAL | SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(BRA, "bra disp:12", 1010dddddddddddd, 1, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(BRAF, "braf rn", 0000nnnn00100011, 2, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(BSR, "bsr disp:12", 1011dddddddddddd, 1, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(BSRF, "bsrf rn", 0000nnnn00000011, 2, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(JMP, "jmp @rm", 0100nnnn00101011, 2, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(JSR, "jsr @rn", 0100nnnn00001011, 2, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(RTS, "rts", 0000000000001011, 2, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
|
||||
|
||||
// system control instructions
|
||||
SH4_INSTR(CLRMAC, "clrmac", 0000000000101000, 1, 0)
|
||||
SH4_INSTR(CLRS, "clrs", 0000000001001000, 1, 0)
|
||||
SH4_INSTR(CLRT, "clrt", 0000000000001000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(LDCSR, "ldc rm, sr", 0100mmmm00001110, 4, OP_FLAG_SET_SR)
|
||||
SH4_INSTR(CLRT, "clrt", 0000000000001000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(LDCSR, "ldc rm, sr", 0100mmmm00001110, 4, SH4_FLAG_SET_SR)
|
||||
SH4_INSTR(LDCGBR, "ldc rm, gbr", 0100mmmm00011110, 3, 0)
|
||||
SH4_INSTR(LDCVBR, "ldc rm, vbr", 0100mmmm00101110, 1, 0)
|
||||
SH4_INSTR(LDCSSR, "ldc rm, ssr", 0100mmmm00111110, 1, 0)
|
||||
SH4_INSTR(LDCSPC, "ldc rm, spc", 0100mmmm01001110, 1, 0)
|
||||
SH4_INSTR(LDCDBR, "ldc rm, dbr", 0100mmmm11111010, 1, 0)
|
||||
SH4_INSTR(LDCRBANK, "ldc.l rm, rn_bank", 0100mmmm1nnn1110, 1, 0)
|
||||
SH4_INSTR(LDCMSR, "ldc.l @rm+, sr", 0100mmmm00000111, 4, OP_FLAG_SET_SR)
|
||||
SH4_INSTR(LDCMSR, "ldc.l @rm+, sr", 0100mmmm00000111, 4, SH4_FLAG_SET_SR)
|
||||
SH4_INSTR(LDCMGBR, "ldc.l @rm+, gbr", 0100mmmm00010111, 3, 0)
|
||||
SH4_INSTR(LDCMVBR, "ldc.l @rm+, vbr", 0100mmmm00100111, 1, 0)
|
||||
SH4_INSTR(LDCMSSR, "ldc.l @rm+, ssr", 0100mmmm00110111, 1, 0)
|
||||
|
@ -162,9 +162,9 @@ SH4_INSTR(OCBI, "ocbi", 0000nnnn10010011, 1, 0)
|
|||
SH4_INSTR(OCBP, "ocbp", 0000nnnn10100011, 1, 0)
|
||||
SH4_INSTR(OCBWB, "ocbwb", 0000nnnn10110011, 1, 0)
|
||||
SH4_INSTR(PREF, "pref @rn", 0000nnnn10000011, 1, 0)
|
||||
SH4_INSTR(RTE, "rte", 0000000000101011, 5, OP_FLAG_BRANCH | OP_FLAG_DELAYED)
|
||||
SH4_INSTR(RTE, "rte", 0000000000101011, 5, SH4_FLAG_BRANCH | SH4_FLAG_DELAYED)
|
||||
SH4_INSTR(SETS, "sets", 0000000001011000, 1, 0)
|
||||
SH4_INSTR(SETT, "sett", 0000000000011000, 1, OP_FLAG_SET_T)
|
||||
SH4_INSTR(SETT, "sett", 0000000000011000, 1, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(SLEEP, "sleep", 0000000000011011, 4, 0)
|
||||
SH4_INSTR(STCSR, "stc sr, rn", 0000nnnn00000010, 2, 0)
|
||||
SH4_INSTR(STCGBR, "stc gbr, rn", 0000nnnn00010010, 2, 0)
|
||||
|
@ -188,7 +188,7 @@ SH4_INSTR(STSPR, "sts pr, rn", 0000nnnn00101010, 2, 0)
|
|||
SH4_INSTR(STSMMACH, "sts.l mach, @-rn", 0100nnnn00000010, 1, 0)
|
||||
SH4_INSTR(STSMMACL, "sts.l macl, @-rn", 0100nnnn00010010, 1, 0)
|
||||
SH4_INSTR(STSMPR, "sts.l pr, @-rn", 0100nnnn00100010, 2, 0)
|
||||
SH4_INSTR(TRAPA, "trapa #imm8", 11000011iiiiiiii, 7, OP_FLAG_BRANCH)
|
||||
SH4_INSTR(TRAPA, "trapa #imm8", 11000011iiiiiiii, 7, SH4_FLAG_BRANCH)
|
||||
|
||||
|
||||
// floating-point single and double precision instructions
|
||||
|
@ -206,8 +206,8 @@ SH4_INSTR(FSTS, "fsts fpul, frn", 1111nnnn00001101, 1, 0)
|
|||
SH4_INSTR(FABS, "fabs frn", 1111nnnn01011101, 1, 0)
|
||||
SH4_INSTR(FSRRA, "fsrra frn", 1111nnnn01111101, 1, 0)
|
||||
SH4_INSTR(FADD, "fadd frm, frn", 1111nnnnmmmm0000, 1, 0)
|
||||
SH4_INSTR(FCMPEQ, "fcmp/eq frm, frn", 1111nnnnmmmm0100, 2, OP_FLAG_SET_T)
|
||||
SH4_INSTR(FCMPGT, "fcmp/gt frm, frn", 1111nnnnmmmm0101, 2, OP_FLAG_SET_T)
|
||||
SH4_INSTR(FCMPEQ, "fcmp/eq frm, frn", 1111nnnnmmmm0100, 2, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(FCMPGT, "fcmp/gt frm, frn", 1111nnnnmmmm0101, 2, SH4_FLAG_SET_T)
|
||||
SH4_INSTR(FDIV, "fdiv frm, frn", 1111nnnnmmmm0011, 1, 0)
|
||||
SH4_INSTR(FLOAT, "float fpul, frn", 1111nnnn00101101, 1, 0)
|
||||
SH4_INSTR(FMAC, "fmac fr0, frm, frn", 1111nnnnmmmm1110, 1, 0)
|
||||
|
@ -225,9 +225,9 @@ SH4_INSTR(FCNVSD, "fcnvsd fpul, drn", 1111nnnn10101101, 1, 0)
|
|||
|
||||
|
||||
// floating-point control instructions
|
||||
SH4_INSTR(LDSFPSCR, "lds rm, fpscr", 0100mmmm01101010, 1, OP_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(LDSFPSCR, "lds rm, fpscr", 0100mmmm01101010, 1, SH4_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(LDSFPUL, "lds rm, fpul", 0100mmmm01011010, 1, 0)
|
||||
SH4_INSTR(LDSMFPSCR, "lds.l @rm+, fpscr", 0100mmmm01100110, 1, OP_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(LDSMFPSCR, "lds.l @rm+, fpscr", 0100mmmm01100110, 1, SH4_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(LDSMFPUL, "lds.l @rm+, fpul", 0100mmmm01010110, 1, 0)
|
||||
SH4_INSTR(STSFPSCR, "sts fpscr, rn", 0000nnnn01101010, 1, 0)
|
||||
SH4_INSTR(STSFPUL, "sts fpul, rn", 0000nnnn01011010, 1, 0)
|
||||
|
@ -240,5 +240,5 @@ SH4_INSTR(STSMFPUL, "sts.l fpul, @-rn", 0100nnnn01010010, 1, 0)
|
|||
SH4_INSTR(FIPR, "fipr fvm, fvn", 1111nnmm11101101, 1, 0)
|
||||
SH4_INSTR(FSCA, "fsca fpul, drn", 1111nnn011111101, 1, 0)
|
||||
SH4_INSTR(FTRV, "ftrv xmtrx, fvn", 1111nn0111111101, 1, 0)
|
||||
SH4_INSTR(FRCHG, "frchg", 1111101111111101, 1, OP_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(FSCHG, "fschg", 1111001111111101, 1, OP_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(FRCHG, "frchg", 1111101111111101, 1, SH4_FLAG_SET_FPSCR)
|
||||
SH4_INSTR(FSCHG, "fschg", 1111001111111101, 1, SH4_FLAG_SET_FPSCR)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
#ifndef SH4_BUILDER_H
|
||||
#define SH4_BUILDER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ir_s;
|
||||
|
||||
void sh4_translate(uint32_t guest_addr, uint8_t *guest_ptr, int size, int flags,
|
||||
struct ir_s *ir);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,696 @@
|
|||
#include "core/math.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
const char *ir_op_names[NUM_OPS] = {
|
||||
#define IR_OP(name) #name,
|
||||
#include "jit/ir/ir_ops.inc"
|
||||
};
|
||||
|
||||
static void *ir_calloc(ir_t *ir, int size) {
|
||||
CHECK_LE(ir->used + size, ir->capacity);
|
||||
uint8_t *ptr = ir->buffer + ir->used;
|
||||
memset(ptr, 0, size);
|
||||
ir->used += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static ir_instr_t *ir_alloc_instr(ir_t *ir, ir_op_t op) {
|
||||
ir_instr_t *instr = ir_calloc(ir, sizeof(ir_instr_t));
|
||||
|
||||
instr->op = op;
|
||||
|
||||
// initialize use links
|
||||
for (int i = 0; i < MAX_INSTR_ARGS; i++) {
|
||||
ir_use_t *use = &instr->used[i];
|
||||
use->instr = instr;
|
||||
use->parg = &instr->arg[i];
|
||||
}
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
static void ir_add_use(ir_value_t *v, ir_use_t *use) {
|
||||
list_add(&v->uses, &use->it);
|
||||
}
|
||||
|
||||
static void ir_remove_use(ir_value_t *v, ir_use_t *use) {
|
||||
list_remove(&v->uses, &use->it);
|
||||
}
|
||||
|
||||
ir_instr_t *ir_append_instr(ir_t *ir, ir_op_t op, ir_type_t result_type) {
|
||||
ir_instr_t *instr = ir_alloc_instr(ir, op);
|
||||
|
||||
// allocate result if needed
|
||||
if (result_type != VALUE_V) {
|
||||
ir_value_t *result = ir_calloc(ir, sizeof(ir_value_t));
|
||||
result->type = result_type;
|
||||
result->def = instr;
|
||||
result->reg = NO_REGISTER;
|
||||
instr->result = result;
|
||||
}
|
||||
|
||||
list_add_after_entry(&ir->instrs, ir->current_instr, it, instr);
|
||||
|
||||
ir->current_instr = instr;
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
void ir_remove_instr(ir_t *ir, ir_instr_t *instr) {
|
||||
// remove arguments from the use lists of their values
|
||||
for (int i = 0; i < MAX_INSTR_ARGS; i++) {
|
||||
ir_value_t *value = instr->arg[i];
|
||||
|
||||
if (value) {
|
||||
ir_remove_use(value, &instr->used[i]);
|
||||
}
|
||||
}
|
||||
|
||||
list_remove(&ir->instrs, &instr->it);
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_i8(ir_t *ir, int8_t c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_I8;
|
||||
v->i8 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_i16(ir_t *ir, int16_t c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_I16;
|
||||
v->i16 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_i32(ir_t *ir, int32_t c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_I32;
|
||||
v->i32 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_i64(ir_t *ir, int64_t c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_I64;
|
||||
v->i64 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_f32(ir_t *ir, float c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_F32;
|
||||
v->f32 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_value_t *ir_alloc_f64(ir_t *ir, double c) {
|
||||
ir_value_t *v = ir_calloc(ir, sizeof(ir_value_t));
|
||||
v->type = VALUE_F64;
|
||||
v->f64 = c;
|
||||
v->reg = NO_REGISTER;
|
||||
return v;
|
||||
}
|
||||
|
||||
ir_local_t *ir_alloc_local(ir_t *ir, ir_type_t type) {
|
||||
// align local to natural size
|
||||
int type_size = ir_type_size(type);
|
||||
ir->locals_size = align_up(ir->locals_size, type_size);
|
||||
|
||||
ir_local_t *l = ir_calloc(ir, sizeof(ir_local_t));
|
||||
l->type = type;
|
||||
l->offset = ir_alloc_i32(ir, ir->locals_size);
|
||||
list_add(&ir->locals, &l->it);
|
||||
|
||||
ir->locals_size += type_size;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void ir_set_arg(ir_t *ir, ir_instr_t *instr, int n, ir_value_t *v) {
|
||||
ir_replace_use(&instr->used[n], v);
|
||||
}
|
||||
|
||||
void ir_set_arg0(ir_t *ir, ir_instr_t *instr, ir_value_t *v) {
|
||||
ir_set_arg(ir, instr, 0, v);
|
||||
}
|
||||
|
||||
void ir_set_arg1(ir_t *ir, ir_instr_t *instr, ir_value_t *v) {
|
||||
ir_set_arg(ir, instr, 1, v);
|
||||
}
|
||||
|
||||
void ir_set_arg2(ir_t *ir, ir_instr_t *instr, ir_value_t *v) {
|
||||
ir_set_arg(ir, instr, 2, v);
|
||||
}
|
||||
|
||||
void ir_replace_use(ir_use_t *use, ir_value_t *other) {
|
||||
if (*use->parg) {
|
||||
ir_remove_use(*use->parg, use);
|
||||
}
|
||||
|
||||
*use->parg = other;
|
||||
|
||||
if (*use->parg) {
|
||||
ir_add_use(*use->parg, use);
|
||||
}
|
||||
}
|
||||
|
||||
// replace all uses of v with other
|
||||
void ir_replace_uses(ir_value_t *v, ir_value_t *other) {
|
||||
CHECK_NE(v, other);
|
||||
|
||||
list_for_each_entry_safe(use, &v->uses, ir_use_t, it) {
|
||||
ir_replace_use(use, other);
|
||||
}
|
||||
}
|
||||
|
||||
bool ir_is_constant(const ir_value_t *v) {
|
||||
return !v->def;
|
||||
}
|
||||
|
||||
uint64_t ir_zext_constant(const ir_value_t *v) {
|
||||
switch (v->type) {
|
||||
case VALUE_I8:
|
||||
return (uint8_t)v->i8;
|
||||
case VALUE_I16:
|
||||
return (uint16_t)v->i16;
|
||||
case VALUE_I32:
|
||||
return (uint32_t)v->i32;
|
||||
case VALUE_I64:
|
||||
return (uint64_t)v->i64;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ir_value_t *ir_load_host(ir_t *ir, ir_value_t *addr, ir_type_t type) {
|
||||
CHECK_EQ(VALUE_I64, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LOAD_HOST, type);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_store_host(ir_t *ir, ir_value_t *addr, ir_value_t *v) {
|
||||
CHECK_EQ(VALUE_I64, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_STORE_HOST, VALUE_V);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
ir_set_arg1(ir, instr, v);
|
||||
}
|
||||
|
||||
ir_value_t *ir_load_fast(ir_t *ir, ir_value_t *addr, ir_type_t type) {
|
||||
CHECK_EQ(VALUE_I32, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LOAD_FAST, type);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_store_fast(ir_t *ir, ir_value_t *addr, ir_value_t *v) {
|
||||
CHECK_EQ(VALUE_I32, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_STORE_FAST, VALUE_V);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
ir_set_arg1(ir, instr, v);
|
||||
}
|
||||
|
||||
ir_value_t *ir_load_slow(ir_t *ir, ir_value_t *addr, ir_type_t type) {
|
||||
CHECK_EQ(VALUE_I32, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LOAD_SLOW, type);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_store_slow(ir_t *ir, ir_value_t *addr, ir_value_t *v) {
|
||||
CHECK_EQ(VALUE_I32, addr->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_STORE_SLOW, VALUE_V);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
ir_set_arg1(ir, instr, v);
|
||||
}
|
||||
|
||||
ir_value_t *ir_load_context(ir_t *ir, size_t offset, ir_type_t type) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LOAD_CONTEXT, type);
|
||||
ir_set_arg0(ir, instr, ir_alloc_i32(ir, offset));
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_store_context(ir_t *ir, size_t offset, ir_value_t *v) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_STORE_CONTEXT, VALUE_V);
|
||||
ir_set_arg0(ir, instr, ir_alloc_i32(ir, offset));
|
||||
ir_set_arg1(ir, instr, v);
|
||||
}
|
||||
|
||||
ir_value_t *ir_load_local(ir_t *ir, ir_local_t *local) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LOAD_LOCAL, local->type);
|
||||
ir_set_arg0(ir, instr, local->offset);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_store_local(ir_t *ir, ir_local_t *local, ir_value_t *v) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_STORE_LOCAL, VALUE_V);
|
||||
ir_set_arg0(ir, instr, local->offset);
|
||||
ir_set_arg1(ir, instr, v);
|
||||
}
|
||||
|
||||
ir_value_t *ir_ftoi(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(ir_is_float(v->type) && is_is_int(dest_type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FTOI, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_itof(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(is_is_int(v->type) && ir_is_float(dest_type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ITOF, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_sext(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(is_is_int(v->type) && is_is_int(dest_type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SEXT, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_zext(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(is_is_int(v->type) && is_is_int(dest_type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ZEXT, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_trunc(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(is_is_int(v->type) && is_is_int(dest_type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_TRUNC, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fext(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(v->type == VALUE_F32 && dest_type == VALUE_F64);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FEXT, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_ftrunc(ir_t *ir, ir_value_t *v, ir_type_t dest_type) {
|
||||
CHECK(v->type == VALUE_F64 && dest_type == VALUE_F32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FTRUNC, dest_type);
|
||||
ir_set_arg0(ir, instr, v);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_select(ir_t *ir, ir_value_t *cond, ir_value_t *t,
|
||||
ir_value_t *f) {
|
||||
CHECK(is_is_int(cond->type) && is_is_int(t->type) && t->type == f->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SELECT, t->type);
|
||||
ir_set_arg0(ir, instr, t);
|
||||
ir_set_arg1(ir, instr, f);
|
||||
ir_set_arg2(ir, instr, cond);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
static ir_value_t *ir_cmp(ir_t *ir, ir_value_t *a, ir_value_t *b,
|
||||
ir_cmp_t type) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_CMP, VALUE_I8);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
ir_set_arg2(ir, instr, ir_alloc_i32(ir, type));
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_eq(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_EQ);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_ne(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_NE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_sge(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_SGE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_sgt(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_SGT);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_uge(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_UGE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_ugt(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_UGT);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_sle(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_SLE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_slt(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_SLT);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_ule(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_ULE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_cmp_ult(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_cmp(ir, a, b, CMP_ULT);
|
||||
}
|
||||
|
||||
static ir_value_t *ir_fcmp(ir_t *ir, ir_value_t *a, ir_value_t *b,
|
||||
ir_cmp_t type) {
|
||||
CHECK(ir_is_float(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FCMP, VALUE_I8);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
ir_set_arg2(ir, instr, ir_alloc_i32(ir, type));
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_eq(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_EQ);
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_ne(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_NE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_ge(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_SGE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_gt(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_SGT);
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_le(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_SLE);
|
||||
}
|
||||
|
||||
ir_value_t *ir_fcmp_lt(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
return ir_fcmp(ir, a, b, CMP_SLT);
|
||||
}
|
||||
|
||||
ir_value_t *ir_add(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ADD, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_sub(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SUB, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_smul(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SMUL, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_umul(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
CHECK(is_is_int(a->type));
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_UMUL, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_div(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_DIV, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_neg(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(is_is_int(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_NEG, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_abs(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(is_is_int(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ABS, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fadd(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(ir_is_float(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FADD, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fsub(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(ir_is_float(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FSUB, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fmul(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(ir_is_float(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FMUL, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fdiv(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(ir_is_float(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FDIV, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fneg(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(ir_is_float(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FNEG, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_fabs(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(ir_is_float(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_FABS, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_sqrt(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(ir_is_float(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SQRT, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_vbroadcast(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(a->type == VALUE_F32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_VBROADCAST, VALUE_V128);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_vadd(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type) {
|
||||
CHECK(ir_is_vector(a->type) && ir_is_vector(b->type));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_VADD, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_vdot(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type) {
|
||||
CHECK(ir_is_vector(a->type) && ir_is_vector(b->type));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_VDOT, el_type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_vmul(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type) {
|
||||
CHECK(ir_is_vector(a->type) && ir_is_vector(b->type));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_VMUL, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_and(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_AND, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_or(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_OR, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_xor(ir_t *ir, ir_value_t *a, ir_value_t *b) {
|
||||
CHECK(is_is_int(a->type) && a->type == b->type);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_XOR, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, b);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_not(ir_t *ir, ir_value_t *a) {
|
||||
CHECK(is_is_int(a->type));
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_NOT, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_shl(ir_t *ir, ir_value_t *a, ir_value_t *n) {
|
||||
CHECK(is_is_int(a->type) && n->type == VALUE_I32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_SHL, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, n);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_shli(ir_t *ir, ir_value_t *a, int n) {
|
||||
return ir_shl(ir, a, ir_alloc_i32(ir, n));
|
||||
}
|
||||
|
||||
ir_value_t *ir_ashr(ir_t *ir, ir_value_t *a, ir_value_t *n) {
|
||||
CHECK(is_is_int(a->type) && n->type == VALUE_I32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ASHR, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, n);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_ashri(ir_t *ir, ir_value_t *a, int n) {
|
||||
return ir_ashr(ir, a, ir_alloc_i32(ir, n));
|
||||
}
|
||||
|
||||
ir_value_t *ir_lshr(ir_t *ir, ir_value_t *a, ir_value_t *n) {
|
||||
CHECK(is_is_int(a->type) && n->type == VALUE_I32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LSHR, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, n);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_lshri(ir_t *ir, ir_value_t *a, int n) {
|
||||
return ir_lshr(ir, a, ir_alloc_i32(ir, n));
|
||||
}
|
||||
|
||||
ir_value_t *ir_ashd(ir_t *ir, ir_value_t *a, ir_value_t *n) {
|
||||
CHECK(a->type == VALUE_I32 && n->type == VALUE_I32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_ASHD, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, n);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
ir_value_t *ir_lshd(ir_t *ir, ir_value_t *a, ir_value_t *n) {
|
||||
CHECK(a->type == VALUE_I32 && n->type == VALUE_I32);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_LSHD, a->type);
|
||||
ir_set_arg0(ir, instr, a);
|
||||
ir_set_arg1(ir, instr, n);
|
||||
return instr->result;
|
||||
}
|
||||
|
||||
void ir_branch(ir_t *ir, ir_value_t *dest) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_BRANCH, VALUE_V);
|
||||
ir_set_arg0(ir, instr, dest);
|
||||
}
|
||||
|
||||
void ir_branch_cond(ir_t *ir, ir_value_t *cond, ir_value_t *true_addr,
|
||||
ir_value_t *false_addr) {
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_BRANCH_COND, VALUE_V);
|
||||
ir_set_arg0(ir, instr, cond);
|
||||
ir_set_arg1(ir, instr, true_addr);
|
||||
ir_set_arg2(ir, instr, false_addr);
|
||||
}
|
||||
|
||||
void ir_call_external_1(ir_t *ir, ir_value_t *addr) {
|
||||
CHECK_EQ(addr->type, VALUE_I64);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_CALL_EXTERNAL, VALUE_V);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
}
|
||||
|
||||
void ir_call_external_2(ir_t *ir, ir_value_t *addr, ir_value_t *arg0) {
|
||||
CHECK_EQ(addr->type, VALUE_I64);
|
||||
CHECK_EQ(arg0->type, VALUE_I64);
|
||||
|
||||
ir_instr_t *instr = ir_append_instr(ir, OP_CALL_EXTERNAL, VALUE_V);
|
||||
ir_set_arg0(ir, instr, addr);
|
||||
ir_set_arg1(ir, instr, arg0);
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
#ifndef IR_BUILDER_H
|
||||
#define IR_BUILDER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "core/assert.h"
|
||||
#include "core/list.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
#define IR_OP(name) OP_##name,
|
||||
#include "jit/ir/ir_ops.inc"
|
||||
#undef IR_OP
|
||||
NUM_OPS
|
||||
} ir_op_t;
|
||||
|
||||
typedef enum {
|
||||
VALUE_V,
|
||||
VALUE_I8,
|
||||
VALUE_I16,
|
||||
VALUE_I32,
|
||||
VALUE_I64,
|
||||
VALUE_F32,
|
||||
VALUE_F64,
|
||||
VALUE_V128,
|
||||
VALUE_NUM,
|
||||
} ir_type_t;
|
||||
|
||||
typedef enum {
|
||||
CMP_EQ,
|
||||
CMP_NE,
|
||||
CMP_SGE,
|
||||
CMP_SGT,
|
||||
CMP_UGE,
|
||||
CMP_UGT,
|
||||
CMP_SLE,
|
||||
CMP_SLT,
|
||||
CMP_ULE,
|
||||
CMP_ULT
|
||||
} ir_cmp_t;
|
||||
|
||||
struct ir_value_s;
|
||||
struct ir_instr_s;
|
||||
|
||||
static const int MAX_INSTR_ARGS = 3;
|
||||
|
||||
// use is a layer of indirection between an instruction and the values it uses
|
||||
// as arguments. this indirection makes it possible to maintain a list for each
|
||||
// value of the arguments that reference it
|
||||
typedef struct ir_use_s {
|
||||
// the instruction that's using the value
|
||||
struct ir_instr_s *instr;
|
||||
|
||||
// pointer to the argument that's using the value. this is used to substitute
|
||||
// a new value for the argument in the case that the original value is
|
||||
// removed (e.g. due to constant propagation)
|
||||
struct ir_value_s **parg;
|
||||
|
||||
list_node_t it;
|
||||
} ir_use_t;
|
||||
|
||||
typedef struct ir_value_s {
|
||||
ir_type_t type;
|
||||
|
||||
union {
|
||||
int8_t i8;
|
||||
int16_t i16;
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
float f32;
|
||||
double f64;
|
||||
};
|
||||
|
||||
// instruction that defines this value (non-constant values)
|
||||
struct ir_instr_s *def;
|
||||
|
||||
// instructions that use this value as an argument
|
||||
list_t uses;
|
||||
|
||||
// host register allocated for this value
|
||||
int reg;
|
||||
|
||||
// generic meta data used by optimization passes
|
||||
intptr_t tag;
|
||||
} ir_value_t;
|
||||
|
||||
typedef struct ir_instr_s {
|
||||
ir_op_t op;
|
||||
|
||||
// values used by each argument. note, the argument / use is split into two
|
||||
// separate members to ease reading the argument value (instr->arg[0] vs
|
||||
// instr->arg[0].value)
|
||||
ir_value_t *arg[MAX_INSTR_ARGS];
|
||||
ir_use_t used[MAX_INSTR_ARGS];
|
||||
|
||||
// result of the instruction. note, instruction results don't consider
|
||||
// themselves users of the value (eases register allocation logic)
|
||||
ir_value_t *result;
|
||||
|
||||
// generic meta data used by optimization passes
|
||||
intptr_t tag;
|
||||
|
||||
list_node_t it;
|
||||
} ir_instr_t;
|
||||
|
||||
// locals are allocated for values that need to be spilled to the stack
|
||||
// during register allocation
|
||||
typedef struct ir_local_s {
|
||||
ir_type_t type;
|
||||
ir_value_t *offset;
|
||||
list_node_t it;
|
||||
} ir_local_t;
|
||||
|
||||
typedef struct ir_s {
|
||||
uint8_t *buffer;
|
||||
int capacity;
|
||||
int used;
|
||||
|
||||
list_t instrs;
|
||||
list_t locals;
|
||||
int locals_size;
|
||||
|
||||
ir_instr_t *current_instr;
|
||||
} ir_t;
|
||||
|
||||
extern const char *ir_op_names[NUM_OPS];
|
||||
|
||||
static const int VALUE_I8_MASK = 1 << VALUE_I8;
|
||||
static const int VALUE_I16_MASK = 1 << VALUE_I16;
|
||||
static const int VALUE_I32_MASK = 1 << VALUE_I32;
|
||||
static const int VALUE_I64_MASK = 1 << VALUE_I64;
|
||||
static const int VALUE_F32_MASK = 1 << VALUE_F32;
|
||||
static const int VALUE_F64_MASK = 1 << VALUE_F64;
|
||||
static const int VALUE_V128_MASK = 1 << VALUE_V128;
|
||||
static const int VALUE_INT_MASK =
|
||||
VALUE_I8_MASK | VALUE_I16_MASK | VALUE_I32_MASK | VALUE_I64_MASK;
|
||||
static const int VALUE_FLOAT_MASK = VALUE_F32_MASK | VALUE_F64_MASK;
|
||||
static const int VALUE_VECTOR_MASK = VALUE_V128_MASK;
|
||||
static const int VALUE_ALL_MASK = VALUE_INT_MASK | VALUE_FLOAT_MASK;
|
||||
|
||||
static const int NO_REGISTER = -1;
|
||||
|
||||
static inline int ir_type_size(ir_type_t type) {
|
||||
switch (type) {
|
||||
case VALUE_I8:
|
||||
return 1;
|
||||
case VALUE_I16:
|
||||
return 2;
|
||||
case VALUE_I32:
|
||||
return 4;
|
||||
case VALUE_I64:
|
||||
return 8;
|
||||
case VALUE_F32:
|
||||
return 4;
|
||||
case VALUE_F64:
|
||||
return 8;
|
||||
case VALUE_V128:
|
||||
return 16;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_is_int(ir_type_t type) {
|
||||
return type == VALUE_I8 || type == VALUE_I16 || type == VALUE_I32 ||
|
||||
type == VALUE_I64;
|
||||
}
|
||||
|
||||
static inline bool ir_is_float(ir_type_t type) {
|
||||
return type == VALUE_F32 || type == VALUE_F64;
|
||||
}
|
||||
|
||||
static inline bool ir_is_vector(ir_type_t type) {
|
||||
return type == VALUE_V128;
|
||||
}
|
||||
|
||||
bool ir_read(FILE *input, struct ir_s *ir);
|
||||
void ir_write(struct ir_s *ir, FILE *output);
|
||||
|
||||
ir_instr_t *ir_append_instr(ir_t *ir, ir_op_t op, ir_type_t result_type);
|
||||
void ir_remove_instr(ir_t *ir, ir_instr_t *instr);
|
||||
|
||||
ir_value_t *ir_alloc_i8(ir_t *ir, int8_t c);
|
||||
ir_value_t *ir_alloc_i16(ir_t *ir, int16_t c);
|
||||
ir_value_t *ir_alloc_i32(ir_t *ir, int32_t c);
|
||||
ir_value_t *ir_alloc_i64(ir_t *ir, int64_t c);
|
||||
ir_value_t *ir_alloc_f32(ir_t *ir, float c);
|
||||
ir_value_t *ir_alloc_f64(ir_t *ir, double c);
|
||||
ir_local_t *ir_alloc_local(ir_t *ir, ir_type_t type);
|
||||
|
||||
void ir_set_arg(ir_t *ir, ir_instr_t *instr, int n, ir_value_t *v);
|
||||
void ir_set_arg0(ir_t *ir, ir_instr_t *instr, ir_value_t *v);
|
||||
void ir_set_arg1(ir_t *ir, ir_instr_t *instr, ir_value_t *v);
|
||||
void ir_set_arg2(ir_t *ir, ir_instr_t *instr, ir_value_t *v);
|
||||
|
||||
void ir_replace_use(ir_use_t *use, ir_value_t *other);
|
||||
void ir_replace_uses(ir_value_t *v, ir_value_t *other);
|
||||
|
||||
bool ir_is_constant(const ir_value_t *v);
|
||||
uint64_t ir_zext_constant(const ir_value_t *v);
|
||||
|
||||
// direct access to host memory
|
||||
ir_value_t *ir_load_host(ir_t *ir, ir_value_t *addr, ir_type_t type);
|
||||
void ir_store_host(ir_t *ir, ir_value_t *addr, ir_value_t *v);
|
||||
|
||||
// guest memory operations
|
||||
ir_value_t *ir_load_fast(ir_t *ir, ir_value_t *addr, ir_type_t type);
|
||||
void ir_store_fast(ir_t *ir, ir_value_t *addr, ir_value_t *v);
|
||||
|
||||
ir_value_t *ir_load_slow(ir_t *ir, ir_value_t *addr, ir_type_t type);
|
||||
void ir_store_slow(ir_t *ir, ir_value_t *addr, ir_value_t *v);
|
||||
|
||||
// context operations
|
||||
ir_value_t *ir_load_context(ir_t *ir, size_t offset, ir_type_t type);
|
||||
void ir_store_context(ir_t *ir, size_t offset, ir_value_t *v);
|
||||
|
||||
// local operations
|
||||
ir_value_t *ir_load_local(ir_t *ir, ir_local_t *local);
|
||||
void ir_store_local(ir_t *ir, ir_local_t *local, ir_value_t *v);
|
||||
|
||||
// cast / conversion operations
|
||||
ir_value_t *ir_ftoi(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_itof(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_sext(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_zext(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_trunc(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_fext(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
ir_value_t *ir_ftrunc(ir_t *ir, ir_value_t *v, ir_type_t dest_type);
|
||||
|
||||
// conditionals
|
||||
ir_value_t *ir_select(ir_t *ir, ir_value_t *cond, ir_value_t *t, ir_value_t *f);
|
||||
ir_value_t *ir_cmp_eq(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_ne(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_sge(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_sgt(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_uge(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_ugt(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_sle(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_slt(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_ule(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_cmp_ult(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_eq(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_ne(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_ge(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_gt(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_le(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fcmp_lt(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
|
||||
// integer math operators
|
||||
ir_value_t *ir_add(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_sub(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_smul(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_umul(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_div(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_neg(ir_t *ir, ir_value_t *a);
|
||||
ir_value_t *ir_abs(ir_t *ir, ir_value_t *a);
|
||||
|
||||
// floating point math operators
|
||||
ir_value_t *ir_fadd(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fsub(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fmul(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fdiv(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_fneg(ir_t *ir, ir_value_t *a);
|
||||
ir_value_t *ir_fabs(ir_t *ir, ir_value_t *a);
|
||||
ir_value_t *ir_sqrt(ir_t *ir, ir_value_t *a);
|
||||
|
||||
// vector math operators
|
||||
ir_value_t *ir_vbroadcast(ir_t *ir, ir_value_t *a);
|
||||
ir_value_t *ir_vadd(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type);
|
||||
ir_value_t *ir_vdot(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type);
|
||||
ir_value_t *ir_vmul(ir_t *ir, ir_value_t *a, ir_value_t *b, ir_type_t el_type);
|
||||
|
||||
// bitwise operations
|
||||
ir_value_t *ir_and(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_or(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_xor(ir_t *ir, ir_value_t *a, ir_value_t *b);
|
||||
ir_value_t *ir_not(ir_t *ir, ir_value_t *a);
|
||||
ir_value_t *ir_shl(ir_t *ir, ir_value_t *a, ir_value_t *n);
|
||||
ir_value_t *ir_shli(ir_t *ir, ir_value_t *a, int n);
|
||||
ir_value_t *ir_ashr(ir_t *ir, ir_value_t *a, ir_value_t *n);
|
||||
ir_value_t *ir_ashri(ir_t *ir, ir_value_t *a, int n);
|
||||
ir_value_t *ir_lshr(ir_t *ir, ir_value_t *a, ir_value_t *n);
|
||||
ir_value_t *ir_lshri(ir_t *ir, ir_value_t *a, int n);
|
||||
ir_value_t *ir_ashd(ir_t *ir, ir_value_t *a, ir_value_t *n);
|
||||
ir_value_t *ir_lshd(ir_t *ir, ir_value_t *a, ir_value_t *n);
|
||||
|
||||
// branches
|
||||
void ir_branch(ir_t *ir, ir_value_t *dest);
|
||||
void ir_branch_cond(ir_t *ir, ir_value_t *cond, ir_value_t *true_addr,
|
||||
ir_value_t *false_addr);
|
||||
|
||||
// calls
|
||||
void ir_call_external_1(ir_t *ir, ir_value_t *addr);
|
||||
void ir_call_external_2(ir_t *ir, ir_value_t *addr, ir_value_t *arg0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,702 +0,0 @@
|
|||
#include <iostream>
|
||||
#include "core/math.h"
|
||||
#include "core/memory.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include "jit/ir/ir_writer.h"
|
||||
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::ir;
|
||||
|
||||
const char *re::jit::ir::Opnames[NUM_OPS] = {
|
||||
#define IR_OP(name) #name,
|
||||
#include "jit/ir/ir_ops.inc"
|
||||
};
|
||||
|
||||
//
|
||||
// Value
|
||||
//
|
||||
Value::Value(ValueType ty) : type_(ty), constant_(false) {}
|
||||
Value::Value(int8_t v) : type_(VALUE_I8), constant_(true), i8_(v) {}
|
||||
Value::Value(int16_t v) : type_(VALUE_I16), constant_(true), i16_(v) {}
|
||||
Value::Value(int32_t v) : type_(VALUE_I32), constant_(true), i32_(v) {}
|
||||
Value::Value(int64_t v) : type_(VALUE_I64), constant_(true), i64_(v) {}
|
||||
Value::Value(float v) : type_(VALUE_F32), constant_(true), f32_(v) {}
|
||||
Value::Value(double v) : type_(VALUE_F64), constant_(true), f64_(v) {}
|
||||
|
||||
uint64_t Value::GetZExtValue() const {
|
||||
switch (type_) {
|
||||
case VALUE_I8:
|
||||
return static_cast<uint8_t>(i8_);
|
||||
case VALUE_I16:
|
||||
return static_cast<uint16_t>(i16_);
|
||||
case VALUE_I32:
|
||||
return static_cast<uint32_t>(i32_);
|
||||
case VALUE_I64:
|
||||
return static_cast<uint64_t>(i64_);
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Value::AddRef(Use *ref) {
|
||||
refs_.Append(ref);
|
||||
}
|
||||
|
||||
void Value::RemoveRef(Use *ref) {
|
||||
refs_.Remove(ref);
|
||||
}
|
||||
|
||||
void Value::ReplaceRefsWith(Value *other) {
|
||||
CHECK_NE(this, other);
|
||||
|
||||
// NOTE set_value will modify refs, be careful iterating
|
||||
auto it = refs_.begin();
|
||||
while (it != refs_.end()) {
|
||||
Use *ref = *(it++);
|
||||
ref->set_value(other);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Use
|
||||
//
|
||||
Use::Use(Instr *instr) : instr_(instr), value_(nullptr) {}
|
||||
|
||||
Use::~Use() {
|
||||
if (value_) {
|
||||
value_->RemoveRef(this);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local
|
||||
//
|
||||
Local::Local(ValueType ty, Value *offset) : type_(ty), offset_(offset) {}
|
||||
|
||||
//
|
||||
// Instr
|
||||
//
|
||||
Instr::Instr(Op op, ValueType result_type)
|
||||
: Value(result_type), op_(op), uses_{{this}, {this}, {this}}, tag_(0) {}
|
||||
|
||||
Instr::~Instr() {}
|
||||
|
||||
//
|
||||
// IRBuilder
|
||||
//
|
||||
IRBuilder::IRBuilder(Arena &arena)
|
||||
: arena_(arena), current_instr_(nullptr), locals_size_(0) {}
|
||||
|
||||
void IRBuilder::Dump(std::ostream &output) const {
|
||||
IRWriter writer;
|
||||
writer.Print(*this, output);
|
||||
}
|
||||
|
||||
void IRBuilder::Dump() const {
|
||||
Dump(std::cout);
|
||||
}
|
||||
|
||||
InsertPoint IRBuilder::GetInsertPoint() {
|
||||
return {current_instr_};
|
||||
}
|
||||
|
||||
void IRBuilder::SetInsertPoint(const InsertPoint &point) {
|
||||
current_instr_ = point.instr;
|
||||
}
|
||||
|
||||
void IRBuilder::RemoveInstr(Instr *instr) {
|
||||
instrs_.Remove(instr);
|
||||
|
||||
// call destructor manually to release value references
|
||||
instr->~Instr();
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LoadHost(Value *addr, ValueType type) {
|
||||
CHECK_EQ(VALUE_I64, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_LOAD_HOST, type);
|
||||
instr->set_arg0(addr);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::StoreHost(Value *addr, Value *v) {
|
||||
CHECK_EQ(VALUE_I64, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_STORE_HOST);
|
||||
instr->set_arg0(addr);
|
||||
instr->set_arg1(v);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LoadFast(Value *addr, ValueType type) {
|
||||
CHECK_EQ(VALUE_I32, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_LOAD_FAST, type);
|
||||
instr->set_arg0(addr);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::StoreFast(Value *addr, Value *v) {
|
||||
CHECK_EQ(VALUE_I32, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_STORE_FAST);
|
||||
instr->set_arg0(addr);
|
||||
instr->set_arg1(v);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LoadSlow(Value *addr, ValueType type) {
|
||||
CHECK_EQ(VALUE_I32, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_LOAD_SLOW, type);
|
||||
instr->set_arg0(addr);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::StoreSlow(Value *addr, Value *v) {
|
||||
CHECK_EQ(VALUE_I32, addr->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_STORE_SLOW);
|
||||
instr->set_arg0(addr);
|
||||
instr->set_arg1(v);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LoadContext(size_t offset, ValueType type) {
|
||||
Instr *instr = AppendInstr(OP_LOAD_CONTEXT, type);
|
||||
instr->set_arg0(AllocConstant((int32_t)offset));
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::StoreContext(size_t offset, Value *v) {
|
||||
Instr *instr = AppendInstr(OP_STORE_CONTEXT);
|
||||
instr->set_arg0(AllocConstant((int32_t)offset));
|
||||
instr->set_arg1(v);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LoadLocal(Local *local) {
|
||||
Instr *instr = AppendInstr(OP_LOAD_LOCAL, local->type());
|
||||
instr->set_arg0(local->offset());
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::StoreLocal(Local *local, Value *v) {
|
||||
Instr *instr = AppendInstr(OP_STORE_LOCAL);
|
||||
instr->set_arg0(local->offset());
|
||||
instr->set_arg1(v);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FToI(Value *v, ValueType dest_type) {
|
||||
CHECK(IsFloatType(v->type()) && IsIntType(dest_type));
|
||||
|
||||
Instr *instr = AppendInstr(OP_FTOI, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::IToF(Value *v, ValueType dest_type) {
|
||||
CHECK(IsIntType(v->type()) && IsFloatType(dest_type));
|
||||
|
||||
Instr *instr = AppendInstr(OP_ITOF, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::SExt(Value *v, ValueType dest_type) {
|
||||
CHECK(IsIntType(v->type()) && IsIntType(dest_type));
|
||||
|
||||
Instr *instr = AppendInstr(OP_SEXT, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::ZExt(Value *v, ValueType dest_type) {
|
||||
CHECK(IsIntType(v->type()) && IsIntType(dest_type));
|
||||
|
||||
Instr *instr = AppendInstr(OP_ZEXT, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Trunc(Value *v, ValueType dest_type) {
|
||||
CHECK(IsIntType(v->type()) && IsIntType(dest_type));
|
||||
|
||||
Instr *instr = AppendInstr(OP_TRUNC, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FExt(Value *v, ValueType dest_type) {
|
||||
CHECK(v->type() == VALUE_F32 && dest_type == VALUE_F64);
|
||||
|
||||
Instr *instr = AppendInstr(OP_FEXT, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FTrunc(Value *v, ValueType dest_type) {
|
||||
CHECK(v->type() == VALUE_F64 && dest_type == VALUE_F32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_FTRUNC, dest_type);
|
||||
instr->set_arg0(v);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Select(Value *cond, Value *t, Value *f) {
|
||||
CHECK(IsIntType(cond->type()) && IsIntType(t->type()) &&
|
||||
t->type() == f->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_SELECT, t->type());
|
||||
instr->set_arg0(t);
|
||||
instr->set_arg1(f);
|
||||
instr->set_arg2(cond);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Cmp(Value *a, Value *b, CmpType type) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_CMP, VALUE_I8);
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
instr->set_arg2(AllocConstant(type));
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpEQ(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_EQ);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpNE(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_NE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpSGE(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_SGE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpSGT(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_SGT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpUGE(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_UGE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpUGT(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_UGT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpSLE(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_SLE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpSLT(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_SLT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpULE(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_ULE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::CmpULT(Value *a, Value *b) {
|
||||
return Cmp(a, b, CMP_ULT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmp(Value *a, Value *b, CmpType type) {
|
||||
CHECK(IsFloatType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_FCMP, VALUE_I8);
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
instr->set_arg2(AllocConstant(type));
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpEQ(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_EQ);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpNE(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_NE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpGE(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_SGE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpGT(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_SGT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpLE(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_SLE);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FCmpLT(Value *a, Value *b) {
|
||||
return FCmp(a, b, CMP_SLT);
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Add(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_ADD, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Sub(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_SUB, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::SMul(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_SMUL, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::UMul(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
CHECK(IsIntType(a->type()));
|
||||
Instr *instr = AppendInstr(OP_UMUL, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Div(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_DIV, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Neg(Value *a) {
|
||||
CHECK(IsIntType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_NEG, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Abs(Value *a) {
|
||||
CHECK(IsIntType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_ABS, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FAdd(Value *a, Value *b) {
|
||||
CHECK(IsFloatType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_FADD, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FSub(Value *a, Value *b) {
|
||||
CHECK(IsFloatType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_FSUB, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FMul(Value *a, Value *b) {
|
||||
CHECK(IsFloatType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_FMUL, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FDiv(Value *a, Value *b) {
|
||||
CHECK(IsFloatType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_FDIV, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FNeg(Value *a) {
|
||||
CHECK(IsFloatType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_FNEG, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::FAbs(Value *a) {
|
||||
CHECK(IsFloatType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_FABS, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Sqrt(Value *a) {
|
||||
CHECK(IsFloatType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_SQRT, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::VBroadcast(Value *a) {
|
||||
CHECK(a->type() == VALUE_F32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_VBROADCAST, VALUE_V128);
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::VAdd(Value *a, Value *b, ValueType el_type) {
|
||||
CHECK(IsVectorType(a->type()) && IsVectorType(b->type()));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_VADD, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::VDot(Value *a, Value *b, ValueType el_type) {
|
||||
CHECK(IsVectorType(a->type()) && IsVectorType(b->type()));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_VDOT, el_type);
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::VMul(Value *a, Value *b, ValueType el_type) {
|
||||
CHECK(IsVectorType(a->type()) && IsVectorType(b->type()));
|
||||
CHECK_EQ(el_type, VALUE_F32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_VMUL, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::And(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_AND, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Or(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_OR, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Xor(Value *a, Value *b) {
|
||||
CHECK(IsIntType(a->type()) && a->type() == b->type());
|
||||
|
||||
Instr *instr = AppendInstr(OP_XOR, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(b);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Not(Value *a) {
|
||||
CHECK(IsIntType(a->type()));
|
||||
|
||||
Instr *instr = AppendInstr(OP_NOT, a->type());
|
||||
instr->set_arg0(a);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Shl(Value *a, Value *n) {
|
||||
CHECK(IsIntType(a->type()) && n->type() == VALUE_I32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_SHL, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(n);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::Shl(Value *a, int n) {
|
||||
return Shl(a, AllocConstant((int32_t)n));
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AShr(Value *a, Value *n) {
|
||||
CHECK(IsIntType(a->type()) && n->type() == VALUE_I32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_ASHR, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(n);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AShr(Value *a, int n) {
|
||||
return AShr(a, AllocConstant((int32_t)n));
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LShr(Value *a, Value *n) {
|
||||
CHECK(IsIntType(a->type()) && n->type() == VALUE_I32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_LSHR, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(n);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LShr(Value *a, int n) {
|
||||
return LShr(a, AllocConstant((int32_t)n));
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AShd(Value *a, Value *n) {
|
||||
CHECK(a->type() == VALUE_I32 && n->type() == VALUE_I32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_ASHD, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(n);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::LShd(Value *a, Value *n) {
|
||||
CHECK(a->type() == VALUE_I32 && n->type() == VALUE_I32);
|
||||
|
||||
Instr *instr = AppendInstr(OP_LSHD, a->type());
|
||||
instr->set_arg0(a);
|
||||
instr->set_arg1(n);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void IRBuilder::Branch(Value *dest) {
|
||||
Instr *instr = AppendInstr(OP_BRANCH);
|
||||
instr->set_arg0(dest);
|
||||
}
|
||||
|
||||
void IRBuilder::BranchCond(Value *cond, Value *true_addr, Value *false_addr) {
|
||||
Instr *instr = AppendInstr(OP_BRANCH_COND);
|
||||
instr->set_arg0(cond);
|
||||
instr->set_arg1(true_addr);
|
||||
instr->set_arg2(false_addr);
|
||||
}
|
||||
|
||||
void IRBuilder::CallExternal1(Value *addr) {
|
||||
CHECK_EQ(addr->type(), VALUE_I64);
|
||||
|
||||
Instr *instr = AppendInstr(OP_CALL_EXTERNAL);
|
||||
instr->set_arg0(addr);
|
||||
}
|
||||
|
||||
void IRBuilder::CallExternal2(Value *addr, Value *arg0) {
|
||||
CHECK_EQ(addr->type(), VALUE_I64);
|
||||
CHECK_EQ(arg0->type(), VALUE_I64);
|
||||
|
||||
Instr *instr = AppendInstr(OP_CALL_EXTERNAL);
|
||||
instr->set_arg0(addr);
|
||||
instr->set_arg1(arg0);
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(uint8_t c) {
|
||||
return AllocConstant((int8_t)c);
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(uint16_t c) {
|
||||
return AllocConstant((int16_t)c);
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(uint32_t c) {
|
||||
return AllocConstant((int32_t)c);
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(uint64_t c) {
|
||||
return AllocConstant((int64_t)c);
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(int8_t c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(int16_t c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(int32_t c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(int64_t c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(float c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value *IRBuilder::AllocConstant(double c) {
|
||||
Value *v = arena_.Alloc<Value>();
|
||||
new (v) Value(c);
|
||||
return v;
|
||||
}
|
||||
|
||||
Local *IRBuilder::AllocLocal(ValueType type) {
|
||||
// align local to natural size
|
||||
int type_size = SizeForType(type);
|
||||
locals_size_ = align_up(locals_size_, type_size);
|
||||
|
||||
Local *l = arena_.Alloc<Local>();
|
||||
new (l) Local(type, AllocConstant(locals_size_));
|
||||
locals_.Append(l);
|
||||
|
||||
locals_size_ += type_size;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AllocInstr(Op op, ValueType result_type) {
|
||||
Instr *instr = arena_.Alloc<Instr>();
|
||||
new (instr) Instr(op, result_type);
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AppendInstr(Op op) {
|
||||
Instr *instr = AllocInstr(op, VALUE_V);
|
||||
instrs_.Insert(current_instr_, instr);
|
||||
current_instr_ = instr;
|
||||
return instr;
|
||||
}
|
||||
|
||||
Instr *IRBuilder::AppendInstr(Op op, ValueType result_type) {
|
||||
Instr *instr = AllocInstr(op, result_type);
|
||||
instrs_.Insert(current_instr_, instr);
|
||||
current_instr_ = instr;
|
||||
return instr;
|
||||
}
|
|
@ -1,550 +0,0 @@
|
|||
#ifndef IR_BUILDER_H
|
||||
#define IR_BUILDER_H
|
||||
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
#include "core/arena.h"
|
||||
#include "core/assert.h"
|
||||
#include "core/intrusive_list.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
|
||||
enum Op {
|
||||
#define IR_OP(name) OP_##name,
|
||||
#include "jit/ir/ir_ops.inc"
|
||||
#undef IR_OP
|
||||
NUM_OPS
|
||||
};
|
||||
|
||||
extern const char *Opnames[NUM_OPS];
|
||||
|
||||
//
|
||||
// values
|
||||
//
|
||||
enum ValueType {
|
||||
VALUE_V,
|
||||
VALUE_I8,
|
||||
VALUE_I16,
|
||||
VALUE_I32,
|
||||
VALUE_I64,
|
||||
VALUE_F32,
|
||||
VALUE_F64,
|
||||
VALUE_V128,
|
||||
VALUE_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
VALUE_I8_MASK = 1 << VALUE_I8,
|
||||
VALUE_I16_MASK = 1 << VALUE_I16,
|
||||
VALUE_I32_MASK = 1 << VALUE_I32,
|
||||
VALUE_I64_MASK = 1 << VALUE_I64,
|
||||
VALUE_F32_MASK = 1 << VALUE_F32,
|
||||
VALUE_F64_MASK = 1 << VALUE_F64,
|
||||
VALUE_V128_MASK = 1 << VALUE_V128,
|
||||
VALUE_INT_MASK =
|
||||
VALUE_I8_MASK | VALUE_I16_MASK | VALUE_I32_MASK | VALUE_I64_MASK,
|
||||
VALUE_FLOAT_MASK = VALUE_F32_MASK | VALUE_F64_MASK,
|
||||
VALUE_VECTOR_MASK = VALUE_V128_MASK,
|
||||
VALUE_ALL_MASK = VALUE_INT_MASK | VALUE_FLOAT_MASK,
|
||||
};
|
||||
|
||||
enum {
|
||||
NO_REGISTER = -1,
|
||||
};
|
||||
|
||||
class Instr;
|
||||
class Use;
|
||||
|
||||
static inline bool IsIntType(ValueType type) {
|
||||
return type == VALUE_I8 || type == VALUE_I16 || type == VALUE_I32 ||
|
||||
type == VALUE_I64;
|
||||
}
|
||||
|
||||
static inline bool IsFloatType(ValueType type) {
|
||||
return type == VALUE_F32 || type == VALUE_F64;
|
||||
}
|
||||
|
||||
static inline bool IsVectorType(ValueType type) {
|
||||
return type == VALUE_V128;
|
||||
}
|
||||
|
||||
static inline int SizeForType(ValueType type) {
|
||||
switch (type) {
|
||||
case VALUE_I8:
|
||||
return 1;
|
||||
case VALUE_I16:
|
||||
return 2;
|
||||
case VALUE_I32:
|
||||
return 4;
|
||||
case VALUE_I64:
|
||||
return 8;
|
||||
case VALUE_F32:
|
||||
return 4;
|
||||
case VALUE_F64:
|
||||
return 8;
|
||||
case VALUE_V128:
|
||||
return 16;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class Value {
|
||||
public:
|
||||
Value(ValueType ty);
|
||||
Value(int8_t v);
|
||||
Value(int16_t v);
|
||||
Value(int32_t v);
|
||||
Value(int64_t v);
|
||||
Value(float v);
|
||||
Value(double v);
|
||||
|
||||
ValueType type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
bool constant() const {
|
||||
return constant_;
|
||||
}
|
||||
|
||||
// defined at the end of the file, Instr is only forward declared at this
|
||||
// point, it can't be static_cast to
|
||||
const Instr *def() const;
|
||||
Instr *def();
|
||||
|
||||
int8_t i8() const {
|
||||
DCHECK(constant_ && type_ == VALUE_I8);
|
||||
return i8_;
|
||||
}
|
||||
int8_t i8() {
|
||||
return static_cast<const Value *>(this)->i8();
|
||||
}
|
||||
int16_t i16() const {
|
||||
DCHECK(constant_ && type_ == VALUE_I16);
|
||||
return i16_;
|
||||
}
|
||||
int16_t i16() {
|
||||
return static_cast<const Value *>(this)->i16();
|
||||
}
|
||||
int32_t i32() const {
|
||||
DCHECK(constant_ && type_ == VALUE_I32);
|
||||
return i32_;
|
||||
}
|
||||
int32_t i32() {
|
||||
return static_cast<const Value *>(this)->i32();
|
||||
}
|
||||
int64_t i64() const {
|
||||
DCHECK(constant_ && type_ == VALUE_I64);
|
||||
return i64_;
|
||||
}
|
||||
int64_t i64() {
|
||||
return static_cast<const Value *>(this)->i64();
|
||||
}
|
||||
float f32() const {
|
||||
DCHECK(constant_ && type_ == VALUE_F32);
|
||||
return f32_;
|
||||
}
|
||||
float f32() {
|
||||
return static_cast<const Value *>(this)->f32();
|
||||
}
|
||||
double f64() const {
|
||||
DCHECK(constant_ && type_ == VALUE_F64);
|
||||
return f64_;
|
||||
}
|
||||
double f64() {
|
||||
return static_cast<const Value *>(this)->f64();
|
||||
}
|
||||
|
||||
const IntrusiveList<Use> &uses() const {
|
||||
return refs_;
|
||||
}
|
||||
IntrusiveList<Use> &uses() {
|
||||
return refs_;
|
||||
}
|
||||
|
||||
int reg() const {
|
||||
return reg_;
|
||||
}
|
||||
void set_reg(int reg) {
|
||||
reg_ = reg;
|
||||
}
|
||||
|
||||
intptr_t tag() const {
|
||||
return tag_;
|
||||
}
|
||||
void set_tag(intptr_t tag) {
|
||||
tag_ = tag;
|
||||
}
|
||||
|
||||
uint64_t GetZExtValue() const;
|
||||
|
||||
void AddRef(Use *ref);
|
||||
void RemoveRef(Use *ref);
|
||||
void ReplaceRefsWith(Value *other);
|
||||
|
||||
private:
|
||||
const ValueType type_;
|
||||
const bool constant_;
|
||||
const union {
|
||||
int8_t i8_;
|
||||
int16_t i16_;
|
||||
int32_t i32_;
|
||||
int64_t i64_;
|
||||
float f32_;
|
||||
double f64_;
|
||||
};
|
||||
IntrusiveList<Use> refs_;
|
||||
// initializing here so each constructor variation doesn't have to
|
||||
int reg_{NO_REGISTER};
|
||||
intptr_t tag_{0};
|
||||
};
|
||||
|
||||
// Use is a layer of indirection between an instruction and a values it uses.
|
||||
// Values maintain a list of all of their uses, making it possible to replace
|
||||
// all uses of a value with a new value during optimizations
|
||||
class Use : public IntrusiveListNode<Use> {
|
||||
public:
|
||||
Use(Instr *instr);
|
||||
~Use();
|
||||
|
||||
const Instr *instr() const {
|
||||
return instr_;
|
||||
}
|
||||
Instr *instr() {
|
||||
return instr_;
|
||||
}
|
||||
|
||||
const Value *value() const {
|
||||
return value_;
|
||||
}
|
||||
Value *value() {
|
||||
return value_;
|
||||
}
|
||||
void set_value(Value *v) {
|
||||
if (value_) {
|
||||
value_->RemoveRef(this);
|
||||
}
|
||||
value_ = v;
|
||||
value_->AddRef(this);
|
||||
}
|
||||
|
||||
private:
|
||||
Instr *instr_;
|
||||
Value *value_;
|
||||
};
|
||||
|
||||
// Templated structs to aid the interpreter / constant propagation handlers
|
||||
template <int T>
|
||||
struct ValueInfo;
|
||||
|
||||
template <>
|
||||
struct ValueInfo<VALUE_V> {
|
||||
typedef void signed_type;
|
||||
constexpr static void (Value::*fn)() = nullptr;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_I8> {
|
||||
typedef int8_t signed_type;
|
||||
typedef uint8_t unsigned_type;
|
||||
constexpr static int8_t (Value::*fn)() = &Value::i8;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_I16> {
|
||||
typedef int16_t signed_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
constexpr static int16_t (Value::*fn)() = &Value::i16;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_I32> {
|
||||
typedef int32_t signed_type;
|
||||
typedef uint32_t unsigned_type;
|
||||
constexpr static int32_t (Value::*fn)() = &Value::i32;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_I64> {
|
||||
typedef int64_t signed_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
constexpr static int64_t (Value::*fn)() = &Value::i64;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_F32> {
|
||||
typedef float signed_type;
|
||||
constexpr static float (Value::*fn)() = &Value::f32;
|
||||
};
|
||||
template <>
|
||||
struct ValueInfo<VALUE_F64> {
|
||||
typedef double signed_type;
|
||||
constexpr static double (Value::*fn)() = &Value::f64;
|
||||
};
|
||||
|
||||
// Locals are allocated for values that need to be spilled to the stack during
|
||||
// register allocation.
|
||||
class Local : public IntrusiveListNode<Local> {
|
||||
public:
|
||||
Local(ValueType ty, Value *offset);
|
||||
|
||||
ValueType type() const {
|
||||
return type_;
|
||||
}
|
||||
Value *offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType type_;
|
||||
Value *offset_;
|
||||
};
|
||||
|
||||
//
|
||||
// instructions
|
||||
//
|
||||
class Instr : public Value, public IntrusiveListNode<Instr> {
|
||||
public:
|
||||
Instr(Op op, ValueType result_type);
|
||||
~Instr();
|
||||
|
||||
Op op() const {
|
||||
return op_;
|
||||
}
|
||||
|
||||
const Value *arg0() const {
|
||||
return arg(0);
|
||||
}
|
||||
Value *arg0() {
|
||||
return arg(0);
|
||||
}
|
||||
void set_arg0(Value *v) {
|
||||
set_arg(0, v);
|
||||
}
|
||||
|
||||
const Value *arg1() const {
|
||||
return arg(1);
|
||||
}
|
||||
Value *arg1() {
|
||||
return arg(1);
|
||||
}
|
||||
void set_arg1(Value *v) {
|
||||
set_arg(1, v);
|
||||
}
|
||||
|
||||
const Value *arg2() const {
|
||||
return arg(2);
|
||||
}
|
||||
Value *arg2() {
|
||||
return arg(2);
|
||||
}
|
||||
void set_arg2(Value *v) {
|
||||
set_arg(2, v);
|
||||
}
|
||||
|
||||
const Value *arg(int i) const {
|
||||
CHECK_LT(i, 3);
|
||||
return uses_[i].value();
|
||||
}
|
||||
Value *arg(int i) {
|
||||
CHECK_LT(i, 3);
|
||||
return uses_[i].value();
|
||||
}
|
||||
void set_arg(int i, Value *v) {
|
||||
CHECK_LT(i, 3);
|
||||
uses_[i].set_value(v);
|
||||
}
|
||||
|
||||
intptr_t tag() const {
|
||||
return tag_;
|
||||
}
|
||||
void set_tag(intptr_t tag) {
|
||||
tag_ = tag;
|
||||
}
|
||||
|
||||
private:
|
||||
Op op_;
|
||||
Use uses_[3];
|
||||
intptr_t tag_;
|
||||
};
|
||||
|
||||
//
|
||||
// IRBuilder
|
||||
//
|
||||
enum CmpType {
|
||||
CMP_EQ,
|
||||
CMP_NE,
|
||||
CMP_SGE,
|
||||
CMP_SGT,
|
||||
CMP_UGE,
|
||||
CMP_UGT,
|
||||
CMP_SLE,
|
||||
CMP_SLT,
|
||||
CMP_ULE,
|
||||
CMP_ULT
|
||||
};
|
||||
|
||||
typedef void (*ExternalFn)(void *);
|
||||
|
||||
struct InsertPoint {
|
||||
Instr *instr;
|
||||
};
|
||||
|
||||
class IRBuilder {
|
||||
friend class IRReader;
|
||||
|
||||
public:
|
||||
IRBuilder(Arena &arena);
|
||||
|
||||
const IntrusiveList<Instr> &instrs() const {
|
||||
return instrs_;
|
||||
}
|
||||
IntrusiveList<Instr> &instrs() {
|
||||
return instrs_;
|
||||
}
|
||||
|
||||
int locals_size() const {
|
||||
return locals_size_;
|
||||
}
|
||||
|
||||
void Dump(std::ostream &output) const;
|
||||
void Dump() const;
|
||||
|
||||
InsertPoint GetInsertPoint();
|
||||
void SetInsertPoint(const InsertPoint &point);
|
||||
|
||||
void RemoveInstr(Instr *instr);
|
||||
|
||||
// direct access to host memory
|
||||
Instr *LoadHost(Value *addr, ValueType type);
|
||||
void StoreHost(Value *addr, Value *v);
|
||||
|
||||
// guest memory operations
|
||||
Instr *LoadFast(Value *addr, ValueType type);
|
||||
void StoreFast(Value *addr, Value *v);
|
||||
|
||||
Instr *LoadSlow(Value *addr, ValueType type);
|
||||
void StoreSlow(Value *addr, Value *v);
|
||||
|
||||
// context operations
|
||||
Instr *LoadContext(size_t offset, ValueType type);
|
||||
void StoreContext(size_t offset, Value *v);
|
||||
|
||||
// local operations
|
||||
Instr *LoadLocal(Local *local);
|
||||
void StoreLocal(Local *local, Value *v);
|
||||
|
||||
// cast / conversion operations
|
||||
Instr *FToI(Value *v, ValueType dest_type);
|
||||
Instr *IToF(Value *v, ValueType dest_type);
|
||||
Instr *SExt(Value *v, ValueType dest_type);
|
||||
Instr *ZExt(Value *v, ValueType dest_type);
|
||||
Instr *Trunc(Value *v, ValueType dest_type);
|
||||
Instr *FExt(Value *v, ValueType dest_type);
|
||||
Instr *FTrunc(Value *v, ValueType dest_type);
|
||||
|
||||
// conditionals
|
||||
Instr *Select(Value *cond, Value *t, Value *f);
|
||||
Instr *CmpEQ(Value *a, Value *b);
|
||||
Instr *CmpNE(Value *a, Value *b);
|
||||
Instr *CmpSGE(Value *a, Value *b);
|
||||
Instr *CmpSGT(Value *a, Value *b);
|
||||
Instr *CmpUGE(Value *a, Value *b);
|
||||
Instr *CmpUGT(Value *a, Value *b);
|
||||
Instr *CmpSLE(Value *a, Value *b);
|
||||
Instr *CmpSLT(Value *a, Value *b);
|
||||
Instr *CmpULE(Value *a, Value *b);
|
||||
Instr *CmpULT(Value *a, Value *b);
|
||||
Instr *FCmpEQ(Value *a, Value *b);
|
||||
Instr *FCmpNE(Value *a, Value *b);
|
||||
Instr *FCmpGE(Value *a, Value *b);
|
||||
Instr *FCmpGT(Value *a, Value *b);
|
||||
Instr *FCmpLE(Value *a, Value *b);
|
||||
Instr *FCmpLT(Value *a, Value *b);
|
||||
|
||||
// integer math operators
|
||||
Instr *Add(Value *a, Value *b);
|
||||
Instr *Sub(Value *a, Value *b);
|
||||
Instr *SMul(Value *a, Value *b);
|
||||
Instr *UMul(Value *a, Value *b);
|
||||
Instr *Div(Value *a, Value *b);
|
||||
Instr *Neg(Value *a);
|
||||
Instr *Abs(Value *a);
|
||||
|
||||
// floating point math operators
|
||||
Instr *FAdd(Value *a, Value *b);
|
||||
Instr *FSub(Value *a, Value *b);
|
||||
Instr *FMul(Value *a, Value *b);
|
||||
Instr *FDiv(Value *a, Value *b);
|
||||
Instr *FNeg(Value *a);
|
||||
Instr *FAbs(Value *a);
|
||||
Instr *Sqrt(Value *a);
|
||||
|
||||
// vector math operators
|
||||
Instr *VBroadcast(Value *a);
|
||||
Instr *VAdd(Value *a, Value *b, ValueType el_type);
|
||||
Instr *VDot(Value *a, Value *b, ValueType el_type);
|
||||
Instr *VMul(Value *a, Value *b, ValueType el_type);
|
||||
|
||||
// bitwise operations
|
||||
Instr *And(Value *a, Value *b);
|
||||
Instr *Or(Value *a, Value *b);
|
||||
Instr *Xor(Value *a, Value *b);
|
||||
Instr *Not(Value *a);
|
||||
Instr *Shl(Value *a, Value *n);
|
||||
Instr *Shl(Value *a, int n);
|
||||
Instr *AShr(Value *a, Value *n);
|
||||
Instr *AShr(Value *a, int n);
|
||||
Instr *LShr(Value *a, Value *n);
|
||||
Instr *LShr(Value *a, int n);
|
||||
Instr *AShd(Value *a, Value *n);
|
||||
Instr *LShd(Value *a, Value *n);
|
||||
|
||||
// branches
|
||||
void Branch(Value *dest);
|
||||
void BranchCond(Value *cond, Value *true_addr, Value *false_addr);
|
||||
|
||||
// calls
|
||||
void CallExternal1(Value *addr);
|
||||
void CallExternal2(Value *addr, Value *arg0);
|
||||
|
||||
// values
|
||||
Value *AllocConstant(uint8_t c);
|
||||
Value *AllocConstant(uint16_t c);
|
||||
Value *AllocConstant(uint32_t c);
|
||||
Value *AllocConstant(uint64_t c);
|
||||
Value *AllocConstant(int8_t c);
|
||||
Value *AllocConstant(int16_t c);
|
||||
Value *AllocConstant(int32_t c);
|
||||
Value *AllocConstant(int64_t c);
|
||||
Value *AllocConstant(float c);
|
||||
Value *AllocConstant(double c);
|
||||
Local *AllocLocal(ValueType type);
|
||||
|
||||
protected:
|
||||
Instr *AllocInstr(Op op, ValueType result_type);
|
||||
Instr *AppendInstr(Op op);
|
||||
Instr *AppendInstr(Op op, ValueType result_type);
|
||||
|
||||
Instr *Cmp(Value *a, Value *b, CmpType type);
|
||||
Instr *FCmp(Value *a, Value *b, CmpType type);
|
||||
|
||||
Arena &arena_;
|
||||
IntrusiveList<Instr> instrs_;
|
||||
Instr *current_instr_;
|
||||
IntrusiveList<Local> locals_;
|
||||
int locals_size_;
|
||||
};
|
||||
|
||||
inline const Instr *Value::def() const {
|
||||
CHECK(!constant_);
|
||||
return static_cast<const Instr *>(this);
|
||||
}
|
||||
|
||||
inline Instr *Value::def() {
|
||||
CHECK(!constant_);
|
||||
return static_cast<Instr *>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,347 @@
|
|||
#include "core/string.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
typedef enum {
|
||||
TOK_EOF,
|
||||
TOK_EOL,
|
||||
TOK_COMMA,
|
||||
TOK_OPERATOR,
|
||||
TOK_TYPE,
|
||||
TOK_INTEGER,
|
||||
TOK_IDENTIFIER,
|
||||
} ir_token_t;
|
||||
|
||||
typedef struct {
|
||||
char s[128];
|
||||
uint64_t i;
|
||||
ir_type_t ty;
|
||||
} ir_lexeme_t;
|
||||
|
||||
typedef struct {
|
||||
FILE *input;
|
||||
ir_token_t tok;
|
||||
ir_lexeme_t val;
|
||||
} ir_parser_t;
|
||||
|
||||
static const char *s_typenames[] = {"", "i8", "i16", "i32",
|
||||
"i64", "f32", "f64", "v128"};
|
||||
static const int s_num_typenames = sizeof(s_typenames) / sizeof(s_typenames[0]);
|
||||
|
||||
static char ir_lex_get(ir_parser_t *p) {
|
||||
return fgetc(p->input);
|
||||
}
|
||||
|
||||
static void ir_lex_unget(ir_parser_t *p, char c) {
|
||||
ungetc(c, p->input);
|
||||
}
|
||||
|
||||
static void ir_lex_next(ir_parser_t *p) {
|
||||
// skip past whitespace characters, except newlines
|
||||
char next;
|
||||
do {
|
||||
next = ir_lex_get(p);
|
||||
} while (isspace(next) && next != '\n');
|
||||
|
||||
// test for end of file
|
||||
if (next == EOF) {
|
||||
strncpy(p->val.s, "", sizeof(p->val.s));
|
||||
p->tok = TOK_EOF;
|
||||
return;
|
||||
}
|
||||
|
||||
// test for newline
|
||||
if (next == '\n') {
|
||||
strncpy(p->val.s, "\n", sizeof(p->val.s));
|
||||
|
||||
// chomp adjacent newlines
|
||||
while (next == '\n') {
|
||||
next = ir_lex_get(p);
|
||||
}
|
||||
ir_lex_unget(p, next);
|
||||
|
||||
p->tok = TOK_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
// test for comma
|
||||
if (next == ',') {
|
||||
strncpy(p->val.s, ",", sizeof(p->val.s));
|
||||
p->tok = TOK_COMMA;
|
||||
return;
|
||||
}
|
||||
|
||||
// test for assignment operator
|
||||
if (next == '=') {
|
||||
strncpy(p->val.s, "=", sizeof(p->val.s));
|
||||
p->tok = TOK_OPERATOR;
|
||||
return;
|
||||
}
|
||||
|
||||
// test for type keyword
|
||||
for (int i = 1; i < s_num_typenames; i++) {
|
||||
const char *typename = s_typenames[i];
|
||||
const char *ptr = typename;
|
||||
char tmp = next;
|
||||
|
||||
// try to match
|
||||
while (*ptr && *ptr == tmp) {
|
||||
tmp = ir_lex_get(p);
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// if the typename matched, return
|
||||
if (!*ptr) {
|
||||
strncpy(p->val.s, typename, sizeof(p->val.s));
|
||||
p->val.ty = i;
|
||||
p->tok = TOK_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
// no match, unget everything
|
||||
if (*ptr && ptr != typename) {
|
||||
ir_lex_unget(p, tmp);
|
||||
ptr--;
|
||||
}
|
||||
|
||||
while (*ptr && ptr != typename) {
|
||||
ir_lex_unget(p, *ptr);
|
||||
ptr--;
|
||||
}
|
||||
}
|
||||
|
||||
// test for hex literal
|
||||
if (next == '0') {
|
||||
next = ir_lex_get(p);
|
||||
|
||||
if (next == 'x') {
|
||||
next = ir_lex_get(p);
|
||||
|
||||
// parse literal
|
||||
p->val.i = 0;
|
||||
while (isxdigit(next)) {
|
||||
p->val.i <<= 4;
|
||||
p->val.i |= xtoi(next);
|
||||
next = ir_lex_get(p);
|
||||
}
|
||||
ir_lex_unget(p, next);
|
||||
|
||||
p->tok = TOK_INTEGER;
|
||||
return;
|
||||
} else {
|
||||
ir_lex_unget(p, next);
|
||||
}
|
||||
}
|
||||
|
||||
// treat anything else as an identifier
|
||||
char *ptr = p->val.s;
|
||||
while (isalpha(next) || isdigit(next) || next == '%' || next == '_') {
|
||||
*ptr++ = next;
|
||||
next = ir_lex_get(p);
|
||||
}
|
||||
ir_lex_unget(p, next);
|
||||
*ptr = 0;
|
||||
|
||||
p->tok = TOK_IDENTIFIER;
|
||||
return;
|
||||
}
|
||||
|
||||
bool ir_parse_type(ir_parser_t *p, ir_t *ir, ir_type_t *type) {
|
||||
if (p->tok != TOK_TYPE) {
|
||||
LOG_INFO("Unexpected token %d when parsing type", p->tok);
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
ir_lex_next(p);
|
||||
|
||||
*type = p->val.ty;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ir_parse_op(ir_parser_t *p, ir_t *ir, ir_op_t *op) {
|
||||
if (p->tok != TOK_IDENTIFIER) {
|
||||
LOG_INFO("Unexpected token %d when parsing op", p->tok);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *op_str = p->val.s;
|
||||
|
||||
// match token against opnames
|
||||
int i;
|
||||
for (i = 0; i < NUM_OPS; i++) {
|
||||
if (!strcasecmp(op_str, ir_op_names[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == NUM_OPS) {
|
||||
LOG_INFO("Unexpected op '%s'", op_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
ir_lex_next(p);
|
||||
|
||||
*op = (ir_op_t)i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ir_parse_value(ir_parser_t *p, ir_t *ir, ir_value_t **value) {
|
||||
// parse value type
|
||||
ir_type_t type;
|
||||
if (!ir_parse_type(p, ir, &type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse value
|
||||
if (p->tok == TOK_IDENTIFIER) {
|
||||
const char *ident = p->val.s;
|
||||
|
||||
if (ident[0] != '%') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// lookup the slot slowly
|
||||
int slot = atoi(&ident[1]);
|
||||
|
||||
ir_instr_t *instr = list_first_entry(&ir->instrs, ir_instr_t, it);
|
||||
while (instr) {
|
||||
if (instr->tag == slot) {
|
||||
break;
|
||||
}
|
||||
instr = list_next_entry(instr, it);
|
||||
}
|
||||
CHECK_NOTNULL(instr);
|
||||
|
||||
*value = instr->result;
|
||||
} else if (p->tok == TOK_INTEGER) {
|
||||
switch (type) {
|
||||
case VALUE_I8: {
|
||||
uint8_t v = (uint8_t)p->val.i;
|
||||
*value = ir_alloc_i8(ir, v);
|
||||
} break;
|
||||
case VALUE_I16: {
|
||||
uint16_t v = (uint16_t)p->val.i;
|
||||
*value = ir_alloc_i16(ir, v);
|
||||
} break;
|
||||
case VALUE_I32: {
|
||||
uint32_t v = (uint32_t)p->val.i;
|
||||
*value = ir_alloc_i32(ir, v);
|
||||
} break;
|
||||
case VALUE_I64: {
|
||||
uint64_t v = (uint64_t)p->val.i;
|
||||
*value = ir_alloc_i64(ir, v);
|
||||
} break;
|
||||
case VALUE_F32: {
|
||||
uint32_t v = (uint32_t)p->val.i;
|
||||
*value = ir_alloc_f32(ir, *(float *)&v);
|
||||
} break;
|
||||
case VALUE_F64: {
|
||||
uint64_t v = (uint64_t)p->val.i;
|
||||
*value = ir_alloc_f64(ir, *(double *)&v);
|
||||
} break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
ir_lex_next(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ir_parse_operator(ir_parser_t *p, ir_t *ir) {
|
||||
const char *op_str = p->val.s;
|
||||
|
||||
if (strcmp(op_str, "=")) {
|
||||
LOG_INFO("Unexpected operator '%s'", op_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
ir_lex_next(p);
|
||||
|
||||
// nothing to do, there's only one operator token
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ir_parse_instr(ir_parser_t *p, ir_t *ir) {
|
||||
int slot = -1;
|
||||
ir_type_t type = VALUE_V;
|
||||
ir_value_t *arg[3] = {};
|
||||
|
||||
// parse result type and slot number
|
||||
if (p->tok == TOK_TYPE) {
|
||||
if (!ir_parse_type(p, ir, &type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *ident = p->val.s;
|
||||
if (ident[0] != '%') {
|
||||
return false;
|
||||
}
|
||||
slot = atoi(&ident[1]);
|
||||
ir_lex_next(p);
|
||||
|
||||
if (!ir_parse_operator(p, ir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// parse op
|
||||
ir_op_t op;
|
||||
if (!ir_parse_op(p, ir, &op)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse arguments
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!ir_parse_value(p, ir, &arg[i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p->tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
|
||||
// eat comma and move onto the next argument
|
||||
ir_lex_next(p);
|
||||
}
|
||||
|
||||
// create instruction
|
||||
ir_instr_t *instr = ir_append_instr(ir, op, type);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ir_set_arg(ir, instr, i, arg[i]);
|
||||
}
|
||||
|
||||
instr->tag = slot;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ir_read(FILE *input, ir_t *ir) {
|
||||
ir_parser_t p = {};
|
||||
p.input = input;
|
||||
|
||||
while (true) {
|
||||
ir_lex_next(&p);
|
||||
|
||||
if (p.tok == TOK_EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ir_parse_instr(&p, ir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,323 +0,0 @@
|
|||
#include "core/string.h"
|
||||
#include "jit/ir/ir_reader.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::ir;
|
||||
|
||||
struct IRType {
|
||||
const char *name;
|
||||
ValueType ty;
|
||||
};
|
||||
|
||||
static IRType s_ir_types[] = {
|
||||
{"i8", VALUE_I8}, {"i16", VALUE_I16}, {"i32", VALUE_I32},
|
||||
{"i64", VALUE_I64}, {"f32", VALUE_F32}, {"f64", VALUE_F64},
|
||||
{"v128", VALUE_V128},
|
||||
};
|
||||
static const int s_num_ir_types = sizeof(s_ir_types) / sizeof(s_ir_types[0]);
|
||||
|
||||
IRLexer::IRLexer(std::istream &input) : input_(input) {}
|
||||
|
||||
IRToken IRLexer::Next() {
|
||||
// skip past whitespace characters, except newlines
|
||||
char next;
|
||||
do {
|
||||
next = Get();
|
||||
} while (isspace(next) && next != '\n');
|
||||
|
||||
// test for end of file
|
||||
if (next == EOF) {
|
||||
strncpy(val_.s, "", sizeof(val_.s));
|
||||
return (tok_ = TOK_EOF);
|
||||
}
|
||||
|
||||
// test for newline
|
||||
if (next == '\n') {
|
||||
strncpy(val_.s, "\n", sizeof(val_.s));
|
||||
|
||||
// chomp adjacent newlines
|
||||
while (next == '\n') {
|
||||
next = Get();
|
||||
}
|
||||
Unget();
|
||||
|
||||
return (tok_ = TOK_EOL);
|
||||
}
|
||||
|
||||
// test for comma
|
||||
if (next == ',') {
|
||||
strncpy(val_.s, ",", sizeof(val_.s));
|
||||
return (tok_ = TOK_COMMA);
|
||||
}
|
||||
|
||||
// test for assignment operator
|
||||
if (next == '=') {
|
||||
strncpy(val_.s, "=", sizeof(val_.s));
|
||||
return (tok_ = TOK_OPERATOR);
|
||||
}
|
||||
|
||||
// test for type keyword
|
||||
for (int i = 0; i < s_num_ir_types; i++) {
|
||||
IRType &ir_type = s_ir_types[i];
|
||||
const char *ptr = ir_type.name;
|
||||
char tmp = next;
|
||||
|
||||
// try to match
|
||||
while (*ptr && *ptr == tmp) {
|
||||
tmp = Get();
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// if we had a match, return
|
||||
if (!*ptr) {
|
||||
strncpy(val_.s, ir_type.name, sizeof(val_.s));
|
||||
val_.ty = ir_type.ty;
|
||||
return (tok_ = TOK_TYPE);
|
||||
}
|
||||
|
||||
// no match, undo
|
||||
while (*ptr && ptr != ir_type.name) {
|
||||
Unget();
|
||||
ptr--;
|
||||
}
|
||||
}
|
||||
|
||||
// test for hex literal
|
||||
if (next == '0') {
|
||||
next = Get();
|
||||
|
||||
if (next == 'x') {
|
||||
next = Get();
|
||||
|
||||
// parse literal
|
||||
val_.i = 0;
|
||||
while (isxdigit(next)) {
|
||||
val_.i <<= 4;
|
||||
val_.i |= xtoi(next);
|
||||
next = Get();
|
||||
}
|
||||
Unget();
|
||||
|
||||
return (tok_ = TOK_INTEGER);
|
||||
} else {
|
||||
Unget();
|
||||
}
|
||||
}
|
||||
|
||||
// treat anything else as an identifier
|
||||
char *ptr = val_.s;
|
||||
while (isalpha(next) || isdigit(next) || next == '%' || next == '_') {
|
||||
*ptr++ = next;
|
||||
next = Get();
|
||||
}
|
||||
Unget();
|
||||
*ptr = 0;
|
||||
|
||||
return (tok_ = TOK_IDENTIFIER);
|
||||
}
|
||||
|
||||
char IRLexer::Get() {
|
||||
return input_.get();
|
||||
}
|
||||
|
||||
void IRLexer::Unget() {
|
||||
input_.unget();
|
||||
}
|
||||
|
||||
bool IRReader::Parse(std::istream &input, IRBuilder &builder) {
|
||||
IRLexer lex(input);
|
||||
|
||||
while (true) {
|
||||
IRToken tok = lex.Next();
|
||||
|
||||
if (tok == TOK_EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ParseInstruction(lex, builder)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRReader::ParseType(IRLexer &lex, IRBuilder &builder, ValueType *type) {
|
||||
if (lex.tok() != TOK_TYPE) {
|
||||
LOG_INFO("Unexpected token %d when parsing type");
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
lex.Next();
|
||||
|
||||
*type = lex.val().ty;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRReader::ParseOp(IRLexer &lex, IRBuilder &builder, Op *op) {
|
||||
if (lex.tok() != TOK_IDENTIFIER) {
|
||||
LOG_INFO("Unexpected token %d when parsing op");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *op_str = lex.val().s;
|
||||
|
||||
// match token against opnames
|
||||
int i;
|
||||
for (i = 0; i < NUM_OPS; i++) {
|
||||
if (!strcasecmp(op_str, Opnames[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// eat token
|
||||
lex.Next();
|
||||
|
||||
if (i == NUM_OPS) {
|
||||
LOG_INFO("Unexpected op '%s'", op_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
*op = static_cast<Op>(i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRReader::ParseValue(IRLexer &lex, IRBuilder &builder, Value **value) {
|
||||
// parse value type
|
||||
ValueType type;
|
||||
if (!ParseType(lex, builder, &type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse value
|
||||
if (lex.tok() == TOK_IDENTIFIER) {
|
||||
const char *ident = lex.val().s;
|
||||
|
||||
if (ident[0] != '%') {
|
||||
return false;
|
||||
}
|
||||
|
||||
int slot = atoi(&ident[1]);
|
||||
auto it = slots_.find(slot);
|
||||
CHECK_NE(it, slots_.end());
|
||||
|
||||
*value = it->second;
|
||||
} else if (lex.tok() == TOK_INTEGER) {
|
||||
switch (type) {
|
||||
case VALUE_I8: {
|
||||
uint8_t v = static_cast<uint8_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(v);
|
||||
} break;
|
||||
case VALUE_I16: {
|
||||
uint16_t v = static_cast<uint16_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(v);
|
||||
} break;
|
||||
case VALUE_I32: {
|
||||
uint32_t v = static_cast<uint32_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(v);
|
||||
} break;
|
||||
case VALUE_I64: {
|
||||
uint64_t v = static_cast<uint64_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(v);
|
||||
} break;
|
||||
case VALUE_F32: {
|
||||
uint32_t v = static_cast<uint32_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(*reinterpret_cast<float *>(&v));
|
||||
} break;
|
||||
case VALUE_F64: {
|
||||
uint64_t v = static_cast<uint64_t>(lex.val().i);
|
||||
*value = builder.AllocConstant(*reinterpret_cast<double *>(&v));
|
||||
} break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
lex.Next();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRReader::ParseOperator(IRLexer &lex, IRBuilder &builder) {
|
||||
const char *op_str = lex.val().s;
|
||||
|
||||
if (strcmp(op_str, "=")) {
|
||||
LOG_INFO("Unexpected operator '%s'", op_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// eat token
|
||||
lex.Next();
|
||||
|
||||
// nothing to do, there's only one operator token
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRReader::ParseInstruction(IRLexer &lex, IRBuilder &builder) {
|
||||
int slot = -1;
|
||||
ValueType type = VALUE_V;
|
||||
Value *arg[3] = {};
|
||||
|
||||
// parse result type and slot number
|
||||
if (lex.tok() == TOK_TYPE) {
|
||||
if (!ParseType(lex, builder, &type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *ident = lex.val().s;
|
||||
if (ident[0] != '%') {
|
||||
return false;
|
||||
}
|
||||
slot = atoi(&ident[1]);
|
||||
lex.Next();
|
||||
|
||||
if (!ParseOperator(lex, builder)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// parse op
|
||||
Op op;
|
||||
if (!ParseOp(lex, builder, &op)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse arguments
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ParseValue(lex, builder, &arg[i]);
|
||||
|
||||
if (lex.tok() != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
|
||||
// eat comma and move onto the next argument
|
||||
lex.Next();
|
||||
}
|
||||
|
||||
// create instruction
|
||||
Instr *instr = builder.AppendInstr(op, type);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!arg[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instr->set_arg(i, arg[i]);
|
||||
}
|
||||
|
||||
// insert instruction into slot if specified
|
||||
if (slot != -1) {
|
||||
slots_.insert(std::make_pair(slot, instr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
#ifndef IR_READER_H
|
||||
#define IR_READER_H
|
||||
|
||||
#include <istream>
|
||||
#include <unordered_map>
|
||||
#include "jit/ir/ir_builder.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
|
||||
enum IRToken {
|
||||
TOK_EOF,
|
||||
TOK_EOL,
|
||||
TOK_COMMA,
|
||||
TOK_OPERATOR,
|
||||
TOK_TYPE,
|
||||
TOK_INTEGER,
|
||||
TOK_IDENTIFIER,
|
||||
};
|
||||
|
||||
struct IRLexeme {
|
||||
char s[128];
|
||||
uint64_t i;
|
||||
ValueType ty;
|
||||
};
|
||||
|
||||
class IRLexer {
|
||||
public:
|
||||
IRLexer(std::istream &input);
|
||||
|
||||
IRToken tok() const {
|
||||
return tok_;
|
||||
}
|
||||
const IRLexeme &val() const {
|
||||
return val_;
|
||||
}
|
||||
|
||||
IRToken Next();
|
||||
|
||||
private:
|
||||
char Get();
|
||||
void Unget();
|
||||
|
||||
std::istream &input_;
|
||||
IRToken tok_;
|
||||
IRLexeme val_;
|
||||
};
|
||||
|
||||
class IRReader {
|
||||
public:
|
||||
bool Parse(std::istream &input, IRBuilder &builder);
|
||||
|
||||
private:
|
||||
bool ParseType(IRLexer &lex, IRBuilder &builder, ValueType *type);
|
||||
bool ParseOp(IRLexer &lex, IRBuilder &builder, Op *op);
|
||||
bool ParseValue(IRLexer &lex, IRBuilder &builder, Value **value);
|
||||
bool ParseOperator(IRLexer &lex, IRBuilder &builder);
|
||||
bool ParseInstruction(IRLexer &lex, IRBuilder &builder);
|
||||
|
||||
std::unordered_map<int, Value *> slots_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,135 @@
|
|||
#include <inttypes.h>
|
||||
#include "core/string.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
static void ir_write_type(ir_type_t type, FILE *output) {
|
||||
switch (type) {
|
||||
case VALUE_I8:
|
||||
fprintf(output, "i8");
|
||||
break;
|
||||
case VALUE_I16:
|
||||
fprintf(output, "i16");
|
||||
break;
|
||||
case VALUE_I32:
|
||||
fprintf(output, "i32");
|
||||
break;
|
||||
case VALUE_I64:
|
||||
fprintf(output, "i64");
|
||||
break;
|
||||
case VALUE_F32:
|
||||
fprintf(output, "f32");
|
||||
break;
|
||||
case VALUE_F64:
|
||||
fprintf(output, "f64");
|
||||
break;
|
||||
case VALUE_V128:
|
||||
fprintf(output, "v128");
|
||||
break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_write_op(ir_op_t op, FILE *output) {
|
||||
const char *name = ir_op_names[op];
|
||||
|
||||
while (*name) {
|
||||
fprintf(output, "%c", tolower(*name));
|
||||
name++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_write_value(const ir_value_t *value, FILE *output) {
|
||||
ir_write_type(value->type, output);
|
||||
|
||||
fprintf(output, " ");
|
||||
|
||||
if (ir_is_constant(value)) {
|
||||
switch (value->type) {
|
||||
case VALUE_I8:
|
||||
// force to int to avoid printing out as a character
|
||||
fprintf(output, "0x%x", value->i8);
|
||||
break;
|
||||
case VALUE_I16:
|
||||
fprintf(output, "0x%x", value->i16);
|
||||
break;
|
||||
case VALUE_I32:
|
||||
fprintf(output, "0x%x", value->i32);
|
||||
break;
|
||||
case VALUE_I64:
|
||||
fprintf(output, "0x%" PRIx64, value->i64);
|
||||
break;
|
||||
case VALUE_F32: {
|
||||
float v = value->f32;
|
||||
fprintf(output, "0x%x", *(uint32_t *)&v);
|
||||
} break;
|
||||
case VALUE_F64: {
|
||||
double v = value->f64;
|
||||
fprintf(output, "0x%" PRIx64, *(uint64_t *)&v);
|
||||
} break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fprintf(output, "%%%d", (int)value->def->tag);
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_write_instr(const ir_instr_t *instr, FILE *output) {
|
||||
// print result value if we have one
|
||||
if (instr->result) {
|
||||
ir_write_value(instr->result, output);
|
||||
fprintf(output, " = ");
|
||||
}
|
||||
|
||||
// print the actual op
|
||||
ir_write_op(instr->op, output);
|
||||
fprintf(output, " ");
|
||||
|
||||
// print each argument
|
||||
bool need_comma = false;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const ir_value_t *arg = instr->arg[i];
|
||||
|
||||
if (!arg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (need_comma) {
|
||||
fprintf(output, ", ");
|
||||
need_comma = false;
|
||||
}
|
||||
|
||||
ir_write_value(arg, output);
|
||||
|
||||
need_comma = true;
|
||||
}
|
||||
|
||||
// fprintf(output, "[tag %" PRId64 ", reg %d]", instr->tag, instr->reg);
|
||||
|
||||
fprintf(output, "\n");
|
||||
}
|
||||
|
||||
static void ir_assign_slots(ir_t *ir) {
|
||||
int next_slot = 0;
|
||||
|
||||
list_for_each_entry(instr, &ir->instrs, ir_instr_t, it) {
|
||||
// don't assign a slot to instructions without a return value
|
||||
if (!instr->result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instr->tag = next_slot++;
|
||||
}
|
||||
}
|
||||
|
||||
void ir_write(ir_t *ir, FILE *output) {
|
||||
ir_assign_slots(ir);
|
||||
|
||||
list_for_each_entry(instr, &ir->instrs, ir_instr_t, it) {
|
||||
ir_write_instr(instr, output);
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
#include <iomanip>
|
||||
#include "jit/ir/ir_writer.h"
|
||||
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::ir;
|
||||
|
||||
void IRWriter::Print(const IRBuilder &builder, std::ostream &output) {
|
||||
slots_.clear();
|
||||
next_slot_ = 0;
|
||||
|
||||
for (auto instr : builder.instrs()) {
|
||||
PrintInstruction(instr, output);
|
||||
}
|
||||
}
|
||||
|
||||
void IRWriter::PrintType(ValueType type, std::ostream &output) const {
|
||||
switch (type) {
|
||||
case VALUE_I8:
|
||||
output << "i8";
|
||||
break;
|
||||
case VALUE_I16:
|
||||
output << "i16";
|
||||
break;
|
||||
case VALUE_I32:
|
||||
output << "i32";
|
||||
break;
|
||||
case VALUE_I64:
|
||||
output << "i64";
|
||||
break;
|
||||
case VALUE_F32:
|
||||
output << "f32";
|
||||
break;
|
||||
case VALUE_F64:
|
||||
output << "f64";
|
||||
break;
|
||||
case VALUE_V128:
|
||||
output << "v128";
|
||||
break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IRWriter::PrintOp(Op op, std::ostream &output) const {
|
||||
const char *name = Opnames[op];
|
||||
|
||||
while (*name) {
|
||||
output << static_cast<char>(tolower(*name));
|
||||
name++;
|
||||
}
|
||||
}
|
||||
|
||||
void IRWriter::PrintValue(const Value *value, std::ostream &output) {
|
||||
PrintType(value->type(), output);
|
||||
|
||||
output << " ";
|
||||
|
||||
if (value->constant()) {
|
||||
switch (value->type()) {
|
||||
case VALUE_I8:
|
||||
// force to int to avoid printing out as a character
|
||||
output << "0x" << std::hex << static_cast<int>(value->i8()) << std::dec;
|
||||
break;
|
||||
case VALUE_I16:
|
||||
output << "0x" << std::hex << value->i16() << std::dec;
|
||||
break;
|
||||
case VALUE_I32:
|
||||
output << "0x" << std::hex << value->i32() << std::dec;
|
||||
break;
|
||||
case VALUE_I64:
|
||||
output << "0x" << std::hex << value->i64() << std::dec;
|
||||
break;
|
||||
case VALUE_F32:
|
||||
output << "0x" << std::hex << value->f32() << std::dec;
|
||||
break;
|
||||
case VALUE_F64:
|
||||
output << "0x" << std::hex << value->f64() << std::dec;
|
||||
break;
|
||||
default:
|
||||
LOG_FATAL("Unexpected value type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
uintptr_t key = reinterpret_cast<uintptr_t>(value);
|
||||
auto it = slots_.find(key);
|
||||
|
||||
if (it == slots_.end()) {
|
||||
auto res = slots_.insert(std::make_pair(key, next_slot_++));
|
||||
it = res.first;
|
||||
}
|
||||
|
||||
output << "%" << it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void IRWriter::PrintInstruction(const Instr *instr, std::ostream &output) {
|
||||
// print result value if we have one
|
||||
if (instr->type() != VALUE_V) {
|
||||
PrintValue(instr, output);
|
||||
output << " = ";
|
||||
}
|
||||
|
||||
// print the actual op
|
||||
PrintOp(instr->op(), output);
|
||||
output << " ";
|
||||
|
||||
// print each argument
|
||||
bool need_comma = false;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!instr->arg(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (need_comma) {
|
||||
output << ", ";
|
||||
need_comma = false;
|
||||
}
|
||||
|
||||
PrintValue(instr->arg(i), output);
|
||||
|
||||
need_comma = true;
|
||||
}
|
||||
|
||||
// output << " [tag " << instr->tag() << ", reg " << instr->reg() << "]";
|
||||
|
||||
output << std::endl;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef IR_WRITER_H
|
||||
#define IR_WRITER_H
|
||||
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
#include "jit/ir/ir_builder.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
|
||||
class IRWriter {
|
||||
public:
|
||||
void Print(const IRBuilder &builder, std::ostream &output);
|
||||
|
||||
private:
|
||||
void PrintType(ValueType type, std::ostream &output) const;
|
||||
void PrintOp(Op op, std::ostream &output) const;
|
||||
void PrintValue(const Value *value, std::ostream &output);
|
||||
void PrintInstruction(const Instr *instr, std::ostream &output);
|
||||
|
||||
std::unordered_map<uintptr_t, int> slots_;
|
||||
int next_slot_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -2,9 +2,6 @@
|
|||
#include <unordered_map>
|
||||
#include "jit/ir/passes/constant_propagation_pass.h"
|
||||
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
typedef void (*FoldFn)(IRBuilder &, Instr *i);
|
||||
|
||||
// specify which arguments must be constant in order for fold operation to run
|
||||
|
@ -28,43 +25,44 @@ int fold_masks[NUM_OPS];
|
|||
// declare a templated callback for an IR operation. note, declaring a
|
||||
// callback does not actually register it. callbacks must be registered
|
||||
// for a particular signature with REGISTER_FOLD
|
||||
#define FOLD(op, mask) \
|
||||
static struct _##op##_init { \
|
||||
_##op##_init() { \
|
||||
fold_masks[OP_##op] = mask; \
|
||||
} \
|
||||
} op##_init; \
|
||||
template <typename R = ValueInfo<VALUE_V>, typename A0 = ValueInfo<VALUE_V>, \
|
||||
typename A1 = ValueInfo<VALUE_V>> \
|
||||
void Handle##op(IRBuilder &builder, Instr *instr)
|
||||
#define FOLD(op, mask) \
|
||||
static struct _##op##_init { \
|
||||
_##op##_init() { \
|
||||
fold_masks[OP_##op] = mask; \
|
||||
} \
|
||||
} op##_init; \
|
||||
template <typename R = ir_value_tInfo<VALUE_V>, \
|
||||
typename A0 = ir_value_tInfo<VALUE_V>, \
|
||||
typename A1 = ir_value_tInfo<VALUE_V>> \
|
||||
void Handle##op(ir_t *ir, Instr *instr)
|
||||
|
||||
// registers a fold callback for the specified signature
|
||||
#define REGISTER_FOLD(op, r, a0, a1) \
|
||||
static struct _cpp_##op##_##r##_##a0##_##a1##_init { \
|
||||
_cpp_##op##_##r##_##a0##_##a1##_init() { \
|
||||
fold_cbs[CALLBACK_IDX(OP_##op, VALUE_##r, VALUE_##a0, VALUE_##a1)] = \
|
||||
&Handle##op<ValueInfo<VALUE_##r>, ValueInfo<VALUE_##a0>, \
|
||||
ValueInfo<VALUE_##a1>>; \
|
||||
} \
|
||||
#define REGISTER_FOLD(op, r, a0, a1) \
|
||||
static struct _cpp_##op##_##r##_##a0##_##a1##_init { \
|
||||
_cpp_##op##_##r##_##a0##_##a1##_init() { \
|
||||
fold_cbs[CALLBACK_IDX(OP_##op, VALUE_##r, VALUE_##a0, VALUE_##a1)] = \
|
||||
&Handle##op<ir_value_tInfo<VALUE_##r>, ir_value_tInfo<VALUE_##a0>, \
|
||||
ir_value_tInfo<VALUE_##a1>>; \
|
||||
} \
|
||||
} cpp_##op##_##r##_##a0##_##a1##_init
|
||||
|
||||
// common helpers for fold functions
|
||||
#define ARG0() (instr->arg0()->*A0::fn)()
|
||||
#define ARG1() (instr->arg1()->*A1::fn)()
|
||||
#define ARG2() (instr->arg2()->*A1::fn)()
|
||||
#define ARG0() (instr->arg[0]->*A0::fn)()
|
||||
#define ARG1() (instr->arg[1]->*A1::fn)()
|
||||
#define ARG2() (instr->arg[2]->*A1::fn)()
|
||||
#define ARG0_UNSIGNED() static_cast<typename A0::unsigned_type>(ARG0())
|
||||
#define ARG1_UNSIGNED() static_cast<typename A1::unsigned_type>(ARG1())
|
||||
#define ARG2_UNSIGNED() static_cast<typename A1::unsigned_type>(ARG2())
|
||||
#define RESULT(expr) \
|
||||
instr->ReplaceRefsWith( \
|
||||
builder.AllocConstant(static_cast<typename R::signed_type>(expr))); \
|
||||
builder.RemoveInstr(instr)
|
||||
#define RESULT(expr) \
|
||||
ir_replace_uses(instr, ir_alloc_constant( \
|
||||
ir, static_cast<typename R::signed_type>(expr))); \
|
||||
ir_remove_instr(instr)
|
||||
|
||||
static FoldFn GetFoldFn(Instr *instr) {
|
||||
auto it = fold_cbs.find(
|
||||
CALLBACK_IDX(instr->op(), instr->type(),
|
||||
instr->arg0() ? (int)instr->arg0()->type() : VALUE_V,
|
||||
instr->arg1() ? (int)instr->arg1()->type() : VALUE_V));
|
||||
CALLBACK_IDX(instr->op, instr->type,
|
||||
instr->arg[0] ? (int)instr->arg[0]->type : VALUE_V,
|
||||
instr->arg[1] ? (int)instr->arg[1]->type : VALUE_V));
|
||||
if (it == fold_cbs.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -72,34 +70,29 @@ static FoldFn GetFoldFn(Instr *instr) {
|
|||
}
|
||||
|
||||
static int GetFoldMask(Instr *instr) {
|
||||
return fold_masks[instr->op()];
|
||||
return fold_masks[instr->op];
|
||||
}
|
||||
|
||||
static int GetConstantSig(Instr *instr) {
|
||||
int cnst_sig = 0;
|
||||
|
||||
if (instr->arg0() && instr->arg0()->constant()) {
|
||||
if (instr->arg[0] && ir_is_constant(instr->arg[0])) {
|
||||
cnst_sig |= ARG0_CNST;
|
||||
}
|
||||
|
||||
if (instr->arg1() && instr->arg1()->constant()) {
|
||||
if (instr->arg[1] && ir_is_constant(instr->arg[1])) {
|
||||
cnst_sig |= ARG1_CNST;
|
||||
}
|
||||
|
||||
if (instr->arg2() && instr->arg2()->constant()) {
|
||||
if (instr->arg[2] && ir_is_constant(instr->arg[2])) {
|
||||
cnst_sig |= ARG2_CNST;
|
||||
}
|
||||
|
||||
return cnst_sig;
|
||||
}
|
||||
|
||||
void ConstantPropagationPass::Run(IRBuilder &builder) {
|
||||
auto it = builder.instrs().begin();
|
||||
auto end = builder.instrs().end();
|
||||
|
||||
while (it != end) {
|
||||
Instr *instr = *(it++);
|
||||
|
||||
void ConstantPropagationPass::Run(ir_t *ir) {
|
||||
list_for_each_entry_safe(instr, &ir->instrs, ir_instr_t, it) {
|
||||
int fold_mask = GetFoldMask(instr);
|
||||
int cnst_sig = GetConstantSig(instr);
|
||||
if (!fold_mask || (cnst_sig & fold_mask) != fold_mask) {
|
||||
|
@ -116,8 +109,8 @@ void ConstantPropagationPass::Run(IRBuilder &builder) {
|
|||
}
|
||||
|
||||
FOLD(SELECT, ARG0_CNST) {
|
||||
instr->ReplaceRefsWith(ARG0() ? instr->arg1() : instr->arg2());
|
||||
builder.RemoveInstr(instr);
|
||||
ir_replace_uses(instr, ARG0() ? instr->arg[1] : instr->arg[2]);
|
||||
ir_remove_instr(ir, instr);
|
||||
}
|
||||
REGISTER_FOLD(SELECT, I8, I8, I8);
|
||||
REGISTER_FOLD(SELECT, I16, I16, I16);
|
||||
|
|
|
@ -3,11 +3,6 @@
|
|||
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
class ConstantPropagationPass : public Pass {
|
||||
public:
|
||||
static const char *NAME = "constprop";
|
||||
|
@ -16,11 +11,7 @@ class ConstantPropagationPass : public Pass {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
void Run(IRBuilder &builder);
|
||||
void Run(struct ir_s *ir);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#include "jit/ir/passes/conversion_elimination_pass.h"
|
||||
#include "jit/ir/passes/pass_stat.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
DEFINE_STAT(num_sext_removed, "Number of sext eliminated");
|
||||
DEFINE_STAT(num_zext_removed, "Number of zext eliminated");
|
||||
DEFINE_STAT(num_trunc_removed, "Number of trunc eliminated");
|
||||
|
||||
const char *cve_name = "cve";
|
||||
|
||||
void cve_run(ir_t *ir) {
|
||||
list_for_each_entry_safe(instr, &ir->instrs, ir_instr_t, it) {
|
||||
// eliminate unnecessary sext / zext operations
|
||||
if (instr->op == OP_LOAD_HOST || instr->op == OP_LOAD_FAST ||
|
||||
instr->op == OP_LOAD_SLOW || instr->op == OP_LOAD_CONTEXT) {
|
||||
ir_type_t memory_type = VALUE_V;
|
||||
bool same_type = true;
|
||||
bool all_sext = true;
|
||||
bool all_zext = true;
|
||||
|
||||
list_for_each_entry(use, &instr->result->uses, ir_use_t, it) {
|
||||
ir_instr_t *use_instr = use->instr;
|
||||
ir_value_t *use_result = use_instr->result;
|
||||
|
||||
if (use_instr->op == OP_SEXT || use_instr->op == OP_ZEXT) {
|
||||
if (memory_type == VALUE_V) {
|
||||
memory_type = use_result->type;
|
||||
}
|
||||
|
||||
if (memory_type != use_result->type) {
|
||||
same_type = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_instr->op != OP_SEXT) {
|
||||
all_sext = false;
|
||||
}
|
||||
|
||||
if (use_instr->op != OP_ZEXT) {
|
||||
all_zext = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (same_type && all_sext) {
|
||||
// TODO implement
|
||||
|
||||
STAT_num_sext_removed++;
|
||||
} else if (same_type && all_zext) {
|
||||
// TODO implement
|
||||
|
||||
STAT_num_zext_removed++;
|
||||
}
|
||||
} else if (instr->op == OP_STORE_HOST || instr->op == OP_STORE_FAST ||
|
||||
instr->op == OP_STORE_SLOW || instr->op == OP_STORE_CONTEXT) {
|
||||
ir_value_t *store_value = instr->arg[1];
|
||||
|
||||
if (store_value->def && store_value->def->op == OP_TRUNC) {
|
||||
// TODO implement
|
||||
|
||||
// note, don't actually remove the truncation as other values may
|
||||
// reference it. let DCE clean it up
|
||||
STAT_num_trunc_removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#include "jit/ir/passes/conversion_elimination_pass.h"
|
||||
|
||||
using namespace re::jit::backend;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
DEFINE_STAT(num_sext_removed, "Number of sext eliminated");
|
||||
DEFINE_STAT(num_zext_removed, "Number of zext eliminated");
|
||||
DEFINE_STAT(num_trunc_removed, "Number of trunc eliminated");
|
||||
|
||||
void ConversionEliminationPass::Run(IRBuilder &builder) {
|
||||
auto it = builder.instrs().begin();
|
||||
auto end = builder.instrs().end();
|
||||
|
||||
while (it != end) {
|
||||
Instr *instr = *(it++);
|
||||
|
||||
// eliminate unnecessary sext / zext operations
|
||||
if (instr->op() == OP_LOAD_HOST || instr->op() == OP_LOAD_FAST ||
|
||||
instr->op() == OP_LOAD_SLOW || instr->op() == OP_LOAD_CONTEXT) {
|
||||
ValueType memory_type = VALUE_V;
|
||||
bool same_type = true;
|
||||
bool all_sext = true;
|
||||
bool all_zext = true;
|
||||
|
||||
for (auto use : instr->uses()) {
|
||||
Instr *use_instr = use->instr();
|
||||
|
||||
if (use_instr->op() == OP_SEXT || use_instr->op() == OP_ZEXT) {
|
||||
if (memory_type == VALUE_V) {
|
||||
memory_type = use_instr->type();
|
||||
}
|
||||
|
||||
if (memory_type != use_instr->type()) {
|
||||
same_type = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_instr->op() != OP_SEXT) {
|
||||
all_sext = false;
|
||||
}
|
||||
|
||||
if (use_instr->op() != OP_ZEXT) {
|
||||
all_zext = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (same_type && all_sext) {
|
||||
// TODO implement
|
||||
|
||||
num_sext_removed++;
|
||||
} else if (same_type && all_zext) {
|
||||
// TODO implement
|
||||
|
||||
num_zext_removed++;
|
||||
}
|
||||
} else if (instr->op() == OP_STORE_HOST || instr->op() == OP_STORE_FAST ||
|
||||
instr->op() == OP_STORE_SLOW ||
|
||||
instr->op() == OP_STORE_CONTEXT) {
|
||||
Value *store_value = instr->arg1();
|
||||
|
||||
if (!store_value->constant()) {
|
||||
Instr *def = store_value->def();
|
||||
|
||||
if (def->op() == OP_TRUNC) {
|
||||
// TODO implement
|
||||
|
||||
// note, don't actually remove the truncation as other values may
|
||||
// reference it. let DCE clean it up
|
||||
num_trunc_removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +1,18 @@
|
|||
#ifndef CONVERSION_ELIMINATION_PASS_H
|
||||
#define CONVERSION_ELIMINATION_PASS_H
|
||||
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
struct ir_s;
|
||||
|
||||
class ConversionEliminationPass : public Pass {
|
||||
public:
|
||||
static constexpr const char *NAME = "cve";
|
||||
extern const char *cve_name;
|
||||
|
||||
const char *name() {
|
||||
return NAME;
|
||||
}
|
||||
void cve_run(struct ir_s *ir);
|
||||
|
||||
void Run(IRBuilder &builder);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#include "jit/ir/passes/dead_code_elimination_pass.h"
|
||||
#include "jit/ir/passes/pass_stat.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
DEFINE_STAT(num_dead_removed, "Number of dead instructions eliminated");
|
||||
|
||||
void dce_run(ir_t *ir) {
|
||||
// iterate in reverse in order to remove groups of dead instructions that
|
||||
// only use eachother
|
||||
list_for_each_entry_safe_reverse(instr, &ir->instrs, ir_instr_t, it) {
|
||||
ir_value_t *result = instr->result;
|
||||
|
||||
if (!result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (list_empty(&result->uses)) {
|
||||
ir_remove_instr(ir, instr);
|
||||
|
||||
STAT_num_dead_removed++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#include "jit/ir/passes/dead_code_elimination_pass.h"
|
||||
|
||||
using namespace re::jit::backend;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
DEFINE_STAT(num_dead_removed, "Number of dead instructions eliminated");
|
||||
|
||||
void DeadCodeEliminationPass::Run(IRBuilder &builder) {
|
||||
// iterate in reverse in order to remove groups of dead instructions that
|
||||
// only use eachother
|
||||
auto it = builder.instrs().rbegin();
|
||||
auto end = builder.instrs().rend();
|
||||
|
||||
while (it != end) {
|
||||
Instr *instr = *(it++);
|
||||
|
||||
if (instr->type() == VALUE_V) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!instr->uses().head()) {
|
||||
builder.RemoveInstr(instr);
|
||||
|
||||
num_dead_removed++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +1,18 @@
|
|||
#ifndef DEAD_CODE_ELIMINATION_PASS_H
|
||||
#define DEAD_CODE_ELIMINATION_PASS_H
|
||||
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
struct ir_s;
|
||||
|
||||
class DeadCodeEliminationPass : public Pass {
|
||||
public:
|
||||
static constexpr const char *NAME = "dce";
|
||||
extern const char *dce_name;
|
||||
|
||||
const char *name() {
|
||||
return NAME;
|
||||
}
|
||||
void dce_run(struct ir_s *ir);
|
||||
|
||||
void Run(IRBuilder &builder);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
#include "jit/ir/passes/load_store_elimination_pass.h"
|
||||
#include "jit/ir/passes/pass_stat.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
DEFINE_STAT(num_loads_removed, "Number of loads eliminated");
|
||||
DEFINE_STAT(num_stores_removed, "Number of stores eliminated");
|
||||
|
||||
const char *lse_name = "lse";
|
||||
|
||||
static const int MAX_OFFSET = 512;
|
||||
|
||||
typedef struct {
|
||||
int offset;
|
||||
ir_value_t *value;
|
||||
} available_t;
|
||||
|
||||
typedef struct { available_t available[MAX_OFFSET]; } lse_t;
|
||||
|
||||
static void lse_clear_available(lse_t *lse) {
|
||||
memset(lse->available, 0, sizeof(lse->available));
|
||||
}
|
||||
|
||||
static ir_value_t *lse_get_available(lse_t *lse, int offset) {
|
||||
CHECK_LT(offset, MAX_OFFSET);
|
||||
|
||||
available_t *entry = &lse->available[offset];
|
||||
|
||||
// entries are added for the entire range of an available value to help with
|
||||
// invalidation. if this entry doesn't start at the requested offset, it's
|
||||
// not actually valid for reuse
|
||||
if (entry->offset != offset) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
static void lse_erase_available(lse_t *lse, int offset, int size) {
|
||||
int begin = offset;
|
||||
int end = offset + size - 1;
|
||||
|
||||
// if the invalidation range intersects with an entry, merge that entry into
|
||||
// the invalidation range
|
||||
available_t *begin_entry = &lse->available[begin];
|
||||
available_t *end_entry = &lse->available[end];
|
||||
|
||||
if (begin_entry->value) {
|
||||
begin = begin_entry->offset;
|
||||
}
|
||||
|
||||
if (end_entry->value) {
|
||||
end = end_entry->offset + ir_type_size(end_entry->value->type) - 1;
|
||||
}
|
||||
|
||||
for (; begin <= end; begin++) {
|
||||
available_t *entry = &lse->available[begin];
|
||||
entry->offset = 0;
|
||||
entry->value = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void lse_set_available(lse_t *lse, int offset, ir_value_t *v) {
|
||||
int size = ir_type_size(v->type);
|
||||
int begin = offset;
|
||||
int end = offset + size - 1;
|
||||
|
||||
lse_erase_available(lse, offset, size);
|
||||
|
||||
// add entries for the entire range to aid in invalidation. only the initial
|
||||
// entry where offset == entry.offset is valid for reuse
|
||||
for (; begin <= end; begin++) {
|
||||
available_t *entry = &lse->available[begin];
|
||||
entry->offset = offset;
|
||||
entry->value = v;
|
||||
}
|
||||
}
|
||||
|
||||
void lse_run(ir_t *ir) {
|
||||
lse_t lse;
|
||||
|
||||
// eliminate redundant loads
|
||||
{
|
||||
lse_clear_available(&lse);
|
||||
|
||||
list_for_each_entry_safe(instr, &ir->instrs, ir_instr_t, it) {
|
||||
if (instr->op == OP_LOAD_CONTEXT) {
|
||||
// if there is already a value available for this offset, reuse it and
|
||||
// remove this redundant load
|
||||
int offset = instr->arg[0]->i32;
|
||||
ir_value_t *available = lse_get_available(&lse, offset);
|
||||
|
||||
if (available && available->type == instr->result->type) {
|
||||
ir_replace_uses(instr->result, available);
|
||||
ir_remove_instr(ir, instr);
|
||||
|
||||
STAT_num_loads_removed++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
lse_set_available(&lse, offset, instr->result);
|
||||
} else if (instr->op == OP_STORE_CONTEXT) {
|
||||
int offset = instr->arg[0]->i32;
|
||||
|
||||
// mark the value being stored as available
|
||||
lse_set_available(&lse, offset, instr->arg[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate dead stores
|
||||
{
|
||||
// iterate in reverse so the current instruction is the one being removed
|
||||
lse_clear_available(&lse);
|
||||
|
||||
list_for_each_entry_safe_reverse(instr, &ir->instrs, ir_instr_t, it) {
|
||||
if (instr->op == OP_LOAD_CONTEXT) {
|
||||
int offset = instr->arg[0]->i32;
|
||||
int size = ir_type_size(instr->result->type);
|
||||
|
||||
lse_erase_available(&lse, offset, size);
|
||||
} else if (instr->op == OP_STORE_CONTEXT) {
|
||||
// if subsequent stores have been made for this offset that would
|
||||
// overwrite it completely, mark instruction as dead
|
||||
int offset = instr->arg[0]->i32;
|
||||
ir_value_t *available = lse_get_available(&lse, offset);
|
||||
int available_size = available ? ir_type_size(available->type) : 0;
|
||||
int store_size = ir_type_size(instr->arg[1]->type);
|
||||
|
||||
if (available_size >= store_size) {
|
||||
ir_remove_instr(ir, instr);
|
||||
|
||||
STAT_num_stores_removed++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
lse_set_available(&lse, offset, instr->arg[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
#include "core/memory.h"
|
||||
#include "jit/ir/passes/load_store_elimination_pass.h"
|
||||
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
DEFINE_STAT(num_loads_removed, "Number of loads eliminated");
|
||||
DEFINE_STAT(num_stores_removed, "Number of stores eliminated");
|
||||
|
||||
LoadStoreEliminationPass::LoadStoreEliminationPass()
|
||||
: available_(nullptr), num_available_(0) {}
|
||||
|
||||
void LoadStoreEliminationPass::Run(IRBuilder &builder) {
|
||||
Reset();
|
||||
|
||||
// eliminate redundant loads
|
||||
{
|
||||
auto it = builder.instrs().begin();
|
||||
auto end = builder.instrs().end();
|
||||
|
||||
ClearAvailable();
|
||||
|
||||
while (it != end) {
|
||||
Instr *instr = *(it++);
|
||||
|
||||
if (instr->op() == OP_LOAD_CONTEXT) {
|
||||
// if there is already a value available for this offset, reuse it and
|
||||
// remove this redundant load
|
||||
int offset = instr->arg0()->i32();
|
||||
Value *available = GetAvailable(offset);
|
||||
|
||||
if (available && available->type() == instr->type()) {
|
||||
instr->ReplaceRefsWith(available);
|
||||
builder.RemoveInstr(instr);
|
||||
|
||||
num_loads_removed++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
SetAvailable(offset, instr);
|
||||
} else if (instr->op() == OP_STORE_CONTEXT) {
|
||||
int offset = instr->arg0()->i32();
|
||||
|
||||
// mark the value being stored as available
|
||||
SetAvailable(offset, instr->arg1());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate dead stores
|
||||
{
|
||||
// iterate in reverse so the current instruction is the one being removed
|
||||
auto it = builder.instrs().rbegin();
|
||||
auto end = builder.instrs().rend();
|
||||
|
||||
ClearAvailable();
|
||||
|
||||
while (it != end) {
|
||||
Instr *instr = *(it++);
|
||||
|
||||
if (instr->op() == OP_LOAD_CONTEXT) {
|
||||
int offset = instr->arg0()->i32();
|
||||
int size = SizeForType(instr->type());
|
||||
|
||||
EraseAvailable(offset, size);
|
||||
} else if (instr->op() == OP_STORE_CONTEXT) {
|
||||
// if subsequent stores have been made for this offset that would
|
||||
// overwrite it completely, mark instruction as dead
|
||||
int offset = instr->arg0()->i32();
|
||||
Value *available = GetAvailable(offset);
|
||||
int available_size = available ? SizeForType(available->type()) : 0;
|
||||
int store_size = SizeForType(instr->arg1()->type());
|
||||
|
||||
if (available_size >= store_size) {
|
||||
builder.RemoveInstr(instr);
|
||||
|
||||
num_stores_removed++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
SetAvailable(offset, instr->arg1());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadStoreEliminationPass::Reset() {
|
||||
ClearAvailable();
|
||||
}
|
||||
|
||||
void LoadStoreEliminationPass::Reserve(int offset) {
|
||||
int reserve = offset + 1;
|
||||
|
||||
if (reserve <= num_available_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// resize availability array to hold new entry
|
||||
available_ = reinterpret_cast<AvailableEntry *>(
|
||||
realloc(available_, reserve * sizeof(AvailableEntry)));
|
||||
|
||||
// memset the newly allocated entries
|
||||
memset(available_ + num_available_, 0,
|
||||
(reserve - num_available_) * sizeof(AvailableEntry));
|
||||
|
||||
num_available_ = reserve;
|
||||
}
|
||||
|
||||
void LoadStoreEliminationPass::ClearAvailable() {
|
||||
if (!available_) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(available_, 0, num_available_ * sizeof(AvailableEntry));
|
||||
}
|
||||
|
||||
Value *LoadStoreEliminationPass::GetAvailable(int offset) {
|
||||
Reserve(offset);
|
||||
|
||||
AvailableEntry &entry = available_[offset];
|
||||
|
||||
// entries are added for the entire range of an available value to help with
|
||||
// invalidation. if this entry doesn't start at the requested offset, it's
|
||||
// not actually valid for reuse
|
||||
if (entry.offset != offset) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
void LoadStoreEliminationPass::EraseAvailable(int offset, int size) {
|
||||
int begin = offset;
|
||||
int end = offset + size - 1;
|
||||
|
||||
Reserve(end);
|
||||
|
||||
// if the invalidation range intersects with an entry, merge that entry into
|
||||
// the invalidation range
|
||||
AvailableEntry &begin_entry = available_[begin];
|
||||
AvailableEntry &end_entry = available_[end];
|
||||
|
||||
if (begin_entry.value) {
|
||||
begin = begin_entry.offset;
|
||||
}
|
||||
|
||||
if (end_entry.value) {
|
||||
end = end_entry.offset + SizeForType(end_entry.value->type()) - 1;
|
||||
}
|
||||
|
||||
for (; begin <= end; begin++) {
|
||||
AvailableEntry &entry = available_[begin];
|
||||
entry.offset = 0;
|
||||
entry.value = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadStoreEliminationPass::SetAvailable(int offset, Value *v) {
|
||||
int size = SizeForType(v->type());
|
||||
int begin = offset;
|
||||
int end = offset + size - 1;
|
||||
|
||||
Reserve(end);
|
||||
|
||||
EraseAvailable(offset, size);
|
||||
|
||||
// add entries for the entire range to aid in invalidation. only the initial
|
||||
// entry where offset == entry.offset is valid for reuse
|
||||
for (; begin <= end; begin++) {
|
||||
AvailableEntry &entry = available_[begin];
|
||||
entry.offset = offset;
|
||||
entry.value = v;
|
||||
}
|
||||
}
|
|
@ -1,45 +1,18 @@
|
|||
#ifndef LOAD_STORE_ELIMINATION_PASS_H
|
||||
#define LOAD_STORE_ELIMINATION_PASS_H
|
||||
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
struct ir_s;
|
||||
|
||||
struct AvailableEntry {
|
||||
int offset;
|
||||
Value *value;
|
||||
};
|
||||
extern const char *lse_name;
|
||||
|
||||
class LoadStoreEliminationPass : public Pass {
|
||||
public:
|
||||
static constexpr const char *NAME = "lse";
|
||||
void lse_run(struct ir_s *ir);
|
||||
|
||||
LoadStoreEliminationPass();
|
||||
|
||||
const char *name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
void Run(IRBuilder &builder);
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
void Reserve(int offset);
|
||||
void ClearAvailable();
|
||||
void EraseAvailable(int offset, int size);
|
||||
Value *GetAvailable(int offset);
|
||||
void SetAvailable(int offset, Value *v);
|
||||
|
||||
AvailableEntry *available_;
|
||||
int num_available_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#include "core/profiler.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
PassRunner::PassRunner() {}
|
||||
|
||||
void PassRunner::AddPass(std::unique_ptr<Pass> pass) {
|
||||
passes_.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
void PassRunner::Run(IRBuilder &builder) {
|
||||
// PROFILER_RUNTIME("PassRunner::Run");
|
||||
|
||||
for (auto &pass : passes_) {
|
||||
// PROFILER_RUNTIME(pass->name());
|
||||
|
||||
pass->Run(builder);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
#ifndef PASS_RUNNER_H
|
||||
#define PASS_RUNNER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "jit/ir/passes/pass_stats.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
class Pass {
|
||||
public:
|
||||
virtual ~Pass() {}
|
||||
|
||||
virtual const char *name() = 0;
|
||||
|
||||
virtual void Run(IRBuilder &builder) = 0;
|
||||
};
|
||||
|
||||
class PassRunner {
|
||||
public:
|
||||
PassRunner();
|
||||
|
||||
void AddPass(std::unique_ptr<Pass> pass);
|
||||
void Run(IRBuilder &builder);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Pass>> passes_;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
#include "core/assert.h"
|
||||
#include "core/math.h"
|
||||
#include "core/string.h"
|
||||
#include "jit/ir/passes/pass_stat.h"
|
||||
|
||||
static list_t s_stats;
|
||||
|
||||
void pass_stat_register(pass_stat_t *stat) {
|
||||
list_add(&s_stats, &stat->it);
|
||||
}
|
||||
|
||||
void pass_stat_unregister(pass_stat_t *stat) {
|
||||
list_remove(&s_stats, &stat->it);
|
||||
}
|
||||
|
||||
void pass_stat_print_all() {
|
||||
LOG_INFO("===-----------------------------------------------------===");
|
||||
LOG_INFO("Pass stats");
|
||||
LOG_INFO("===-----------------------------------------------------===");
|
||||
|
||||
int w = 0;
|
||||
list_for_each_entry(stat, &s_stats, pass_stat_t, it) {
|
||||
int l = (int)strlen(stat->desc);
|
||||
w = MAX(l, w);
|
||||
}
|
||||
|
||||
list_for_each_entry(stat, &s_stats, pass_stat_t, it) {
|
||||
LOG_INFO("%-*s %d", w, stat->desc, stat->n);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef PASS_STATS_H
|
||||
#define PASS_STATS_H
|
||||
|
||||
#include "core/constructor.h"
|
||||
#include "core/list.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEFINE_STAT(name, desc) \
|
||||
static int STAT_##name; \
|
||||
static pass_stat_t STAT_T_##name = {#name, desc, &STAT_##name, {}}; \
|
||||
CONSTRUCTOR(STAT_REGISTER_##name) { \
|
||||
pass_stat_register(&STAT_T_##name); \
|
||||
} \
|
||||
DESTRUCTOR(STAT_UNREGISTER_##name) { \
|
||||
pass_stat_unregister(&STAT_T_##name); \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
int *n;
|
||||
list_node_t it;
|
||||
} pass_stat_t;
|
||||
|
||||
void pass_stat_register(pass_stat_t *stat);
|
||||
void pass_stat_unregister(pass_stat_t *stat);
|
||||
void pass_stat_print_all();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,66 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include "core/assert.h"
|
||||
#include "core/string.h"
|
||||
#include "jit/ir/passes/pass_stats.h"
|
||||
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
static Stat *s_head_stat;
|
||||
|
||||
static void RegisterStat(Stat *stat) {
|
||||
stat->next = s_head_stat;
|
||||
s_head_stat = stat;
|
||||
}
|
||||
|
||||
static void UnregisterStat(Stat *stat) {
|
||||
Stat **tmp = &s_head_stat;
|
||||
|
||||
while (*tmp) {
|
||||
Stat **next = &(*tmp)->next;
|
||||
|
||||
if (*tmp == stat) {
|
||||
*tmp = *next;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = next;
|
||||
}
|
||||
}
|
||||
|
||||
Stat::Stat(const char *desc) : desc(desc), n(0), next(nullptr) {
|
||||
RegisterStat(this);
|
||||
}
|
||||
|
||||
Stat::~Stat() {
|
||||
UnregisterStat(this);
|
||||
}
|
||||
|
||||
void DumpStats() {
|
||||
LOG_INFO("===-----------------------------------------------------===");
|
||||
LOG_INFO("Pass stats");
|
||||
LOG_INFO("===-----------------------------------------------------===");
|
||||
|
||||
int w = 0;
|
||||
Stat *stat = s_head_stat;
|
||||
while (stat) {
|
||||
int l = static_cast<int>(strlen(stat->desc));
|
||||
w = std::max(l, w);
|
||||
stat = stat->next;
|
||||
}
|
||||
|
||||
stat = s_head_stat;
|
||||
while (stat) {
|
||||
LOG_INFO("%-*s %d", w, stat->desc, stat->n);
|
||||
stat = stat->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#ifndef PASS_STATS_H
|
||||
#define PASS_STATS_H
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
#define DEFINE_STAT(name, desc) static Stat name(desc);
|
||||
|
||||
struct Stat {
|
||||
Stat(const char *desc);
|
||||
~Stat();
|
||||
|
||||
const char *desc;
|
||||
int n;
|
||||
Stat *next;
|
||||
|
||||
operator int() const {
|
||||
return n;
|
||||
}
|
||||
|
||||
const Stat &operator=(int v) {
|
||||
n = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Stat &operator++(int v) {
|
||||
n++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Stat &operator+=(int v) {
|
||||
n += v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Stat &operator--() {
|
||||
n--;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Stat &operator-=(int v) {
|
||||
n -= v;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void DumpStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,387 @@
|
|||
#include "core/mm_heap.h"
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/ir/passes/pass_stat.h"
|
||||
#include "jit/ir/passes/register_allocation_pass.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
DEFINE_STAT(num_spills, "Number of registers spilled");
|
||||
|
||||
static const int MAX_REGISTERS = 32;
|
||||
|
||||
typedef struct {
|
||||
ir_instr_t *instr;
|
||||
ir_instr_t *reused;
|
||||
ir_use_t *start;
|
||||
ir_use_t *end;
|
||||
ir_use_t *next;
|
||||
int reg;
|
||||
} interval_t;
|
||||
|
||||
typedef struct {
|
||||
int free_regs[MAX_REGISTERS];
|
||||
int num_free_regs;
|
||||
|
||||
interval_t *live_intervals[MAX_REGISTERS];
|
||||
int num_live_intervals;
|
||||
} register_set_t;
|
||||
|
||||
typedef struct {
|
||||
// canonical backend register information
|
||||
const register_def_t *registers;
|
||||
int num_registers;
|
||||
|
||||
// allocation state
|
||||
register_set_t int_registers;
|
||||
register_set_t float_registers;
|
||||
register_set_t vector_registers;
|
||||
|
||||
// intervals, keyed by register
|
||||
interval_t intervals[MAX_REGISTERS];
|
||||
} ra_t;
|
||||
|
||||
static int ra_get_ordinal(const ir_instr_t *i) {
|
||||
return (int)i->tag;
|
||||
}
|
||||
|
||||
static void ra_set_ordinal(ir_instr_t *i, int ordinal) {
|
||||
i->tag = (intptr_t)ordinal;
|
||||
}
|
||||
|
||||
static int ra_pop_register(register_set_t *set) {
|
||||
if (!set->num_free_regs) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
return set->free_regs[--set->num_free_regs];
|
||||
}
|
||||
|
||||
static void ra_push_register(register_set_t *set, int reg) {
|
||||
set->free_regs[set->num_free_regs++] = reg;
|
||||
}
|
||||
|
||||
static bool ra_interval_cmp(const interval_t *lhs, const interval_t *rhs) {
|
||||
return !lhs->next ||
|
||||
ra_get_ordinal(lhs->next->instr) < ra_get_ordinal(rhs->next->instr);
|
||||
};
|
||||
|
||||
static interval_t *ra_head_interval(register_set_t *set) {
|
||||
if (!set->num_live_intervals) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mm_type *it = mm_find_min((mm_type *)set->live_intervals,
|
||||
set->num_live_intervals, (mm_cmp)&ra_interval_cmp);
|
||||
return *it;
|
||||
}
|
||||
|
||||
static interval_t *ra_tail_interval(register_set_t *set) {
|
||||
if (!set->num_live_intervals) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mm_type *it = mm_find_max((mm_type *)set->live_intervals,
|
||||
set->num_live_intervals, (mm_cmp)&ra_interval_cmp);
|
||||
return *it;
|
||||
}
|
||||
|
||||
static void ra_pop_head_interval(register_set_t *set) {
|
||||
mm_pop_min((mm_type *)set->live_intervals, set->num_live_intervals,
|
||||
(mm_cmp)&ra_interval_cmp);
|
||||
set->num_live_intervals--;
|
||||
}
|
||||
|
||||
static void ra_pop_tail_interval(register_set_t *set) {
|
||||
mm_pop_max((mm_type *)set->live_intervals, set->num_live_intervals,
|
||||
(mm_cmp)&ra_interval_cmp);
|
||||
set->num_live_intervals--;
|
||||
}
|
||||
|
||||
static void ra_insert_interval(register_set_t *set, interval_t *interval) {
|
||||
set->live_intervals[set->num_live_intervals++] = interval;
|
||||
mm_push((mm_type *)set->live_intervals, set->num_live_intervals,
|
||||
(mm_cmp)&ra_interval_cmp);
|
||||
}
|
||||
|
||||
static register_set_t *ra_get_register_set(ra_t *ra, ir_type_t type) {
|
||||
if (is_is_int(type)) {
|
||||
return &ra->int_registers;
|
||||
}
|
||||
|
||||
if (ir_is_float(type)) {
|
||||
return &ra->float_registers;
|
||||
}
|
||||
|
||||
if (ir_is_vector(type)) {
|
||||
return &ra->vector_registers;
|
||||
}
|
||||
|
||||
LOG_FATAL("Unexpected value type");
|
||||
}
|
||||
|
||||
static int ra_alloc_blocked_register(ra_t *ra, ir_t *ir, ir_instr_t *instr) {
|
||||
ir_instr_t *insert_point = ir->current_instr;
|
||||
register_set_t *set = ra_get_register_set(ra, instr->result->type);
|
||||
|
||||
// spill the register who's next use is furthest away from start
|
||||
interval_t *interval = ra_tail_interval(set);
|
||||
ra_pop_tail_interval(set);
|
||||
|
||||
// the interval's value needs to be filled back from from the stack before
|
||||
// its next use
|
||||
ir_use_t *next_use = interval->next;
|
||||
ir_use_t *prev_use = list_prev_entry(next_use, it);
|
||||
CHECK(next_use,
|
||||
"Register being spilled has no next use, why wasn't it expired?");
|
||||
|
||||
// allocate a place on the stack to spill the value
|
||||
ir_local_t *local = ir_alloc_local(ir, interval->instr->result->type);
|
||||
|
||||
// insert load before next use
|
||||
ir->current_instr = list_prev_entry(next_use->instr, it);
|
||||
ir_value_t *load_value = ir_load_local(ir, local);
|
||||
ir_instr_t *load_instr = load_value->def;
|
||||
|
||||
// assign the load a valid ordinal
|
||||
int load_ordinal = ra_get_ordinal(list_prev_entry(load_instr, it)) + 1;
|
||||
CHECK_LT(load_ordinal, ra_get_ordinal(list_next_entry(load_instr, it)));
|
||||
ra_set_ordinal(load_instr, load_ordinal);
|
||||
|
||||
// update uses of interval->instr after the next use to use the new value
|
||||
// filled from the stack. this code asssumes that the uses were previously
|
||||
// sorted inside of Run()
|
||||
while (next_use) {
|
||||
// cache off next next since calling set_value will modify the linked list
|
||||
// pointers
|
||||
ir_use_t *next_next_use = list_next_entry(next_use, it);
|
||||
ir_replace_use(next_use, load_instr->result);
|
||||
next_use = next_next_use;
|
||||
}
|
||||
|
||||
// insert spill after prev use, note that order here is extremely important.
|
||||
// interval->instr's use list has already been sorted, and when the save
|
||||
// instruction is created and added as a use, the sorted order will be
|
||||
// invalidated. because of this, the save instruction needs to be added after
|
||||
// the load instruction has updated the sorted uses
|
||||
ir_instr_t *after = NULL;
|
||||
|
||||
if (prev_use) {
|
||||
// there is a previous useerence, insert store after it
|
||||
CHECK(list_next_entry(prev_use, it) == NULL,
|
||||
"All future uses should have been replaced");
|
||||
after = prev_use->instr;
|
||||
} else {
|
||||
// there is no previous use, insert store immediately after definition
|
||||
CHECK(list_empty(&interval->instr->result->uses),
|
||||
"All future uses should have been replaced");
|
||||
after = interval->instr;
|
||||
}
|
||||
|
||||
ir->current_instr = after;
|
||||
ir_store_local(ir, local, interval->instr->result);
|
||||
|
||||
// since the interval that this store belongs to has now expired, there's no
|
||||
// need to assign an ordinal to it
|
||||
|
||||
// reuse the old interval
|
||||
interval->instr = instr;
|
||||
interval->reused = NULL;
|
||||
interval->start = list_first_entry(&instr->result->uses, ir_use_t, it);
|
||||
interval->end = list_last_entry(&instr->result->uses, ir_use_t, it);
|
||||
interval->next = interval->start;
|
||||
ra_insert_interval(set, interval);
|
||||
|
||||
// reset insert point
|
||||
ir->current_instr = insert_point;
|
||||
|
||||
STAT_num_spills++;
|
||||
|
||||
return interval->reg;
|
||||
}
|
||||
|
||||
static int ra_alloc_free_register(ra_t *ra, ir_instr_t *instr) {
|
||||
register_set_t *set = ra_get_register_set(ra, instr->result->type);
|
||||
|
||||
// get the first free register for this value type
|
||||
int reg = ra_pop_register(set);
|
||||
if (reg == NO_REGISTER) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// add interval
|
||||
interval_t *interval = &ra->intervals[reg];
|
||||
interval->instr = instr;
|
||||
interval->reused = NULL;
|
||||
interval->start = list_first_entry(&instr->result->uses, ir_use_t, it);
|
||||
interval->end = list_last_entry(&instr->result->uses, ir_use_t, it);
|
||||
interval->next = interval->start;
|
||||
interval->reg = reg;
|
||||
ra_insert_interval(set, interval);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
// If the first argument isn't used after this instruction, its register
|
||||
// can be reused to take advantage of many architectures supporting
|
||||
// operations where the destination is the first argument.
|
||||
// TODO could reorder arguments for communicative binary ops and do this
|
||||
// with the second argument as well
|
||||
static int ra_reuse_arg_register(ra_t *ra, ir_t *ir, ir_instr_t *instr) {
|
||||
if (!instr->arg[0]) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
int prefered = instr->arg[0]->reg;
|
||||
if (prefered == NO_REGISTER) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// make sure the register can hold the result type
|
||||
const register_def_t *r = &ra->registers[prefered];
|
||||
if (!(r->value_types & (1 << instr->result->type))) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// if the argument's register is used after this instruction, it's not
|
||||
// trivial to reuse
|
||||
interval_t *interval = &ra->intervals[prefered];
|
||||
if (list_next_entry(interval->next, it)) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// the argument's register is not used after the current instruction, so the
|
||||
// register can be reused for the result. note, since the interval min/max
|
||||
// heap does not support removal of an arbitrary interval, the interval
|
||||
// removal must be deferred. since there are no more uses, the interval will
|
||||
// expire on the next call to ExpireOldintervals, and then immediately
|
||||
// requeued by setting the reused property
|
||||
interval->reused = instr;
|
||||
|
||||
return prefered;
|
||||
}
|
||||
|
||||
static void ra_expire_set(ra_t *ra, register_set_t *set, ir_instr_t *instr) {
|
||||
while (true) {
|
||||
interval_t *interval = ra_head_interval(set);
|
||||
if (!interval) {
|
||||
break;
|
||||
}
|
||||
|
||||
// intervals are sorted by their next use, once one fails to expire or
|
||||
// advance, they all will
|
||||
if (interval->next &&
|
||||
ra_get_ordinal(interval->next->instr) >= ra_get_ordinal(instr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// remove interval from the sorted set
|
||||
ra_pop_head_interval(set);
|
||||
|
||||
// if there are more uses, advance the next use and reinsert the interval
|
||||
// into the correct position
|
||||
if (interval->next && list_next_entry(interval->next, it)) {
|
||||
interval->next = list_next_entry(interval->next, it);
|
||||
ra_insert_interval(set, interval);
|
||||
}
|
||||
// if there are no more uses, but the register has been reused by
|
||||
// ReuseArgRegister, requeue the interval at this time
|
||||
else if (interval->reused) {
|
||||
ir_instr_t *reused = interval->reused;
|
||||
interval->instr = reused;
|
||||
interval->reused = NULL;
|
||||
interval->start = list_first_entry(&reused->result->uses, ir_use_t, it);
|
||||
interval->end = list_last_entry(&reused->result->uses, ir_use_t, it);
|
||||
interval->next = interval->start;
|
||||
ra_insert_interval(set, interval);
|
||||
}
|
||||
// if there are no other uses, free the register assigned to this
|
||||
// interval
|
||||
else {
|
||||
ra_push_register(set, interval->reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_expire_intervals(ra_t *ra, ir_instr_t *instr) {
|
||||
ra_expire_set(ra, &ra->int_registers, instr);
|
||||
ra_expire_set(ra, &ra->float_registers, instr);
|
||||
ra_expire_set(ra, &ra->vector_registers, instr);
|
||||
}
|
||||
|
||||
static int use_cmp(const list_node_t *a_it, const list_node_t *b_it) {
|
||||
ir_use_t *a = list_entry(a_it, ir_use_t, it);
|
||||
ir_use_t *b = list_entry(b_it, ir_use_t, it);
|
||||
return ra_get_ordinal(a->instr) - ra_get_ordinal(b->instr);
|
||||
}
|
||||
|
||||
static void ra_assign_ordinals(ir_t *ir) {
|
||||
// assign each instruction an ordinal. these ordinals are used to describe
|
||||
// the live range of a particular value
|
||||
int ordinal = 0;
|
||||
|
||||
list_for_each_entry(instr, &ir->instrs, ir_instr_t, it) {
|
||||
ra_set_ordinal(instr, ordinal);
|
||||
|
||||
// space out ordinals to leave available values for instructions inserted
|
||||
// by AllocBlockedRegister. there should never be an ir op with more than
|
||||
// 10 arguments to spill registers for
|
||||
ordinal += 10;
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_init_sets(ra_t *ra, const register_def_t *registers,
|
||||
int num_registers) {
|
||||
ra->registers = registers;
|
||||
ra->num_registers = num_registers;
|
||||
|
||||
for (int i = 0; i < ra->num_registers; i++) {
|
||||
const register_def_t *r = &ra->registers[i];
|
||||
|
||||
if (r->value_types == VALUE_INT_MASK) {
|
||||
ra_push_register(&ra->int_registers, i);
|
||||
} else if (r->value_types == VALUE_FLOAT_MASK) {
|
||||
ra_push_register(&ra->float_registers, i);
|
||||
} else if (r->value_types == VALUE_VECTOR_MASK) {
|
||||
ra_push_register(&ra->vector_registers, i);
|
||||
} else {
|
||||
LOG_FATAL("Unsupported register value mask");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_run(ir_t *ir, const register_def_t *registers, int num_registers) {
|
||||
ra_t ra = {};
|
||||
|
||||
ra_init_sets(&ra, registers, num_registers);
|
||||
|
||||
ra_assign_ordinals(ir);
|
||||
|
||||
list_for_each_entry(instr, &ir->instrs, ir_instr_t, it) {
|
||||
ir_value_t *result = instr->result;
|
||||
|
||||
// only allocate registers for results, assume constants can always be
|
||||
// encoded as immediates or that the backend has registers reserved
|
||||
// for storing the constants
|
||||
if (!result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// sort the instruction's use list
|
||||
list_sort(&result->uses, &use_cmp);
|
||||
|
||||
// expire any old intervals, freeing up the registers they claimed
|
||||
ra_expire_intervals(&ra, instr);
|
||||
|
||||
// first, try and reuse the register of one of the incoming arguments
|
||||
int reg = ra_reuse_arg_register(&ra, ir, instr);
|
||||
if (reg == NO_REGISTER) {
|
||||
// else, allocate a new register for the result
|
||||
reg = ra_alloc_free_register(&ra, instr);
|
||||
if (reg == NO_REGISTER) {
|
||||
// if a register couldn't be allocated, spill a register and try again
|
||||
reg = ra_alloc_blocked_register(&ra, ir, instr);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_NE(reg, NO_REGISTER, "Failed to allocate register");
|
||||
result->reg = reg;
|
||||
}
|
||||
}
|
|
@ -1,377 +0,0 @@
|
|||
#include "core/minmax_heap.h"
|
||||
#include "jit/ir/passes/register_allocation_pass.h"
|
||||
|
||||
using namespace re::jit::backend;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
|
||||
DEFINE_STAT(num_spills, "Number of registers spilled");
|
||||
|
||||
static inline int GetOrdinal(const Instr *i) {
|
||||
return (int)i->tag();
|
||||
}
|
||||
|
||||
static inline void SetOrdinal(Instr *i, int ordinal) {
|
||||
i->set_tag((intptr_t)ordinal);
|
||||
}
|
||||
|
||||
static inline bool RegisterCanStore(const Register &r, ValueType type) {
|
||||
return r.value_types & (1 << type);
|
||||
}
|
||||
|
||||
struct LiveIntervalSort {
|
||||
bool operator()(const Interval *lhs, const Interval *rhs) const {
|
||||
return !lhs->next ||
|
||||
GetOrdinal(lhs->next->instr()) < GetOrdinal(rhs->next->instr());
|
||||
}
|
||||
};
|
||||
|
||||
RegisterSet::RegisterSet(int max_registers) {
|
||||
free_ = new int[max_registers];
|
||||
live_ = new Interval *[max_registers];
|
||||
}
|
||||
|
||||
RegisterSet::~RegisterSet() {
|
||||
delete[] free_;
|
||||
delete[] live_;
|
||||
}
|
||||
|
||||
void RegisterSet::Clear() {
|
||||
num_free_ = 0;
|
||||
num_live_ = 0;
|
||||
}
|
||||
|
||||
int RegisterSet::PopRegister() {
|
||||
if (!num_free_) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
return free_[--num_free_];
|
||||
}
|
||||
|
||||
void RegisterSet::PushRegister(int reg) {
|
||||
free_[num_free_++] = reg;
|
||||
}
|
||||
|
||||
Interval *RegisterSet::HeadInterval() {
|
||||
if (!num_live_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = re::mmheap_find_min(live_, live_ + num_live_, LiveIntervalSort());
|
||||
return *it;
|
||||
}
|
||||
|
||||
Interval *RegisterSet::TailInterval() {
|
||||
if (!num_live_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = re::mmheap_find_max(live_, live_ + num_live_, LiveIntervalSort());
|
||||
return *it;
|
||||
}
|
||||
|
||||
void RegisterSet::PopHeadInterval() {
|
||||
re::mmheap_pop_min(live_, live_ + num_live_, LiveIntervalSort());
|
||||
num_live_--;
|
||||
}
|
||||
|
||||
void RegisterSet::PopTailInterval() {
|
||||
re::mmheap_pop_max(live_, live_ + num_live_, LiveIntervalSort());
|
||||
num_live_--;
|
||||
}
|
||||
|
||||
void RegisterSet::InsertInterval(Interval *interval) {
|
||||
live_[num_live_++] = interval;
|
||||
re::mmheap_push(live_, live_ + num_live_, LiveIntervalSort());
|
||||
}
|
||||
|
||||
RegisterAllocationPass::RegisterAllocationPass(
|
||||
const backend::Register *registers, int num_registers)
|
||||
: int_registers_(num_registers),
|
||||
float_registers_(num_registers),
|
||||
vector_registers_(num_registers) {
|
||||
registers_ = registers;
|
||||
num_registers_ = num_registers;
|
||||
|
||||
intervals_ = new Interval[num_registers_];
|
||||
}
|
||||
|
||||
RegisterAllocationPass::~RegisterAllocationPass() {
|
||||
delete[] intervals_;
|
||||
}
|
||||
|
||||
void RegisterAllocationPass::Run(IRBuilder &builder) {
|
||||
Reset();
|
||||
|
||||
AssignOrdinals(builder);
|
||||
|
||||
for (auto instr : builder.instrs()) {
|
||||
// only allocate registers for results, assume constants can always be
|
||||
// encoded as immediates or that the backend has registers reserved
|
||||
// for storing the constants
|
||||
if (instr->type() == VALUE_V) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// sort the instruction's ref list
|
||||
instr->uses().Sort([](const Use *a, const Use *b) {
|
||||
return GetOrdinal(a->instr()) < GetOrdinal(b->instr());
|
||||
});
|
||||
|
||||
// expire any old intervals, freeing up the registers they claimed
|
||||
ExpireOldIntervals(instr);
|
||||
|
||||
// first, try and reuse the register of one of the incoming arguments
|
||||
int reg = ReuseArgRegister(builder, instr);
|
||||
if (reg == NO_REGISTER) {
|
||||
// else, allocate a new register for the result
|
||||
reg = AllocFreeRegister(instr);
|
||||
if (reg == NO_REGISTER) {
|
||||
// if a register couldn't be allocated, spill a register and try again
|
||||
reg = AllocBlockedRegister(builder, instr);
|
||||
CHECK_NE(reg, NO_REGISTER, "Failed to allocate register");
|
||||
}
|
||||
}
|
||||
|
||||
instr->set_reg(reg);
|
||||
}
|
||||
}
|
||||
|
||||
RegisterSet &RegisterAllocationPass::GetRegisterSet(ValueType type) {
|
||||
if (IsIntType(type)) {
|
||||
return int_registers_;
|
||||
}
|
||||
|
||||
if (IsFloatType(type)) {
|
||||
return float_registers_;
|
||||
}
|
||||
|
||||
if (IsVectorType(type)) {
|
||||
return vector_registers_;
|
||||
}
|
||||
|
||||
LOG_FATAL("Unexpected value type");
|
||||
}
|
||||
|
||||
void RegisterAllocationPass::Reset() {
|
||||
int_registers_.Clear();
|
||||
float_registers_.Clear();
|
||||
vector_registers_.Clear();
|
||||
|
||||
for (int i = 0; i < num_registers_; i++) {
|
||||
const Register &r = registers_[i];
|
||||
|
||||
if (r.value_types == VALUE_INT_MASK) {
|
||||
int_registers_.PushRegister(i);
|
||||
} else if (r.value_types == VALUE_FLOAT_MASK) {
|
||||
float_registers_.PushRegister(i);
|
||||
} else if (r.value_types == VALUE_VECTOR_MASK) {
|
||||
vector_registers_.PushRegister(i);
|
||||
} else {
|
||||
LOG_FATAL("Unsupported register value mask");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterAllocationPass::AssignOrdinals(IRBuilder &builder) {
|
||||
// assign each instruction an ordinal. these ordinals are used to describe
|
||||
// the live range of a particular value
|
||||
int ordinal = 0;
|
||||
for (auto instr : builder.instrs()) {
|
||||
SetOrdinal(instr, ordinal);
|
||||
|
||||
// space out ordinals to leave available values for instructions inserted
|
||||
// by AllocBlockedRegister. there should never be an ir op with more than
|
||||
// 10 arguments to spill registers for
|
||||
ordinal += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterAllocationPass::ExpireOldIntervals(Instr *instr) {
|
||||
auto expire_set = [&](RegisterSet &set) {
|
||||
while (true) {
|
||||
Interval *interval = set.HeadInterval();
|
||||
if (!interval) {
|
||||
break;
|
||||
}
|
||||
|
||||
// intervals are sorted by their next use, once one fails to expire or
|
||||
// advance, they all will
|
||||
if (interval->next &&
|
||||
GetOrdinal(interval->next->instr()) >= GetOrdinal(instr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// remove interval from the sorted set
|
||||
set.PopHeadInterval();
|
||||
|
||||
// if there are more uses, advance the next use and reinsert the interval
|
||||
// into the correct position
|
||||
if (interval->next && interval->next->next()) {
|
||||
interval->next = interval->next->next();
|
||||
set.InsertInterval(interval);
|
||||
}
|
||||
// if there are no more uses, but the register has been reused by
|
||||
// ReuseArgRegister, requeue the interval at this time
|
||||
else if (interval->reused) {
|
||||
Instr *reused = interval->reused;
|
||||
interval->instr = reused;
|
||||
interval->reused = nullptr;
|
||||
interval->start = reused->uses().head();
|
||||
interval->end = reused->uses().tail();
|
||||
interval->next = interval->start;
|
||||
set.InsertInterval(interval);
|
||||
}
|
||||
// if there are no other uses, free the register assigned to this
|
||||
// interval
|
||||
else {
|
||||
set.PushRegister(interval->reg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expire_set(int_registers_);
|
||||
expire_set(float_registers_);
|
||||
expire_set(vector_registers_);
|
||||
}
|
||||
|
||||
// If the first argument isn't used after this instruction, its register
|
||||
// can be reused to take advantage of many architectures supporting
|
||||
// operations where the destination is the first argument.
|
||||
// TODO could reorder arguments for communicative binary ops and do this
|
||||
// with the second argument as well
|
||||
int RegisterAllocationPass::ReuseArgRegister(IRBuilder &builder, Instr *instr) {
|
||||
if (!instr->arg0() || instr->arg0()->constant()) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
int prefered = instr->arg0()->reg();
|
||||
if (prefered == NO_REGISTER) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// make sure the register can hold the result type
|
||||
const Register &r = registers_[prefered];
|
||||
if (!RegisterCanStore(r, instr->type())) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// if the argument's register is used after this instruction, it's not
|
||||
// trivial to reuse
|
||||
Interval *interval = &intervals_[prefered];
|
||||
if (interval->next->next()) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// the argument's register is not used after the current instruction, so the
|
||||
// register can be reused for the result. note, since the interval min/max
|
||||
// heap does not support removal of an arbitrary interval, the interval
|
||||
// removal must be deferred. since there are no more references, the interval
|
||||
// will expire on the next call to ExpireOldIntervals, and then immediately
|
||||
// requeued by setting the reused property
|
||||
interval->reused = instr;
|
||||
|
||||
return prefered;
|
||||
}
|
||||
|
||||
int RegisterAllocationPass::AllocFreeRegister(Instr *instr) {
|
||||
RegisterSet &set = GetRegisterSet(instr->type());
|
||||
|
||||
// get the first free register for this value type
|
||||
int reg = set.PopRegister();
|
||||
if (reg == NO_REGISTER) {
|
||||
return NO_REGISTER;
|
||||
}
|
||||
|
||||
// add interval
|
||||
Interval *interval = &intervals_[reg];
|
||||
interval->instr = instr;
|
||||
interval->reused = nullptr;
|
||||
interval->start = instr->uses().head();
|
||||
interval->end = instr->uses().tail();
|
||||
interval->next = interval->start;
|
||||
interval->reg = reg;
|
||||
set.InsertInterval(interval);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
int RegisterAllocationPass::AllocBlockedRegister(IRBuilder &builder,
|
||||
Instr *instr) {
|
||||
InsertPoint insert_point = builder.GetInsertPoint();
|
||||
RegisterSet &set = GetRegisterSet(instr->type());
|
||||
|
||||
// spill the register who's next use is furthest away from start
|
||||
Interval *interval = set.TailInterval();
|
||||
set.PopTailInterval();
|
||||
|
||||
// the interval's value needs to be filled back from from the stack before
|
||||
// its next use
|
||||
Use *next_ref = interval->next;
|
||||
Use *prev_ref = next_ref->prev();
|
||||
CHECK(next_ref,
|
||||
"Register being spilled has no next use, why wasn't it expired?");
|
||||
|
||||
// allocate a place on the stack to spill the value
|
||||
Local *local = builder.AllocLocal(interval->instr->type());
|
||||
|
||||
// insert load before next use
|
||||
builder.SetInsertPoint({next_ref->instr()->prev()});
|
||||
Instr *load_instr = builder.LoadLocal(local);
|
||||
|
||||
// assign the load a valid ordinal
|
||||
int load_ordinal = GetOrdinal(load_instr->prev()) + 1;
|
||||
CHECK_LT(load_ordinal, GetOrdinal(load_instr->next()));
|
||||
SetOrdinal(load_instr, load_ordinal);
|
||||
|
||||
// update references to interval->instr after the next use to use the new
|
||||
// value filled from the stack. this code asssumes that the refs were
|
||||
// previously sorted inside of Run().
|
||||
while (next_ref) {
|
||||
// cache off next next since calling set_value will modify the linked list
|
||||
// pointers
|
||||
Use *next_next_ref = next_ref->next();
|
||||
next_ref->set_value(load_instr);
|
||||
next_ref = next_next_ref;
|
||||
}
|
||||
|
||||
// insert spill after prev use, note that order here is extremely important.
|
||||
// interval->instr's ref list has already been sorted, and when the save
|
||||
// instruction is created and added as a reference, the sorted order will be
|
||||
// invalidated. because of this, the save instruction needs to be added after
|
||||
// the load instruction has updated the sorted references.
|
||||
Instr *after = nullptr;
|
||||
|
||||
if (prev_ref) {
|
||||
// there is a previous reference, insert store after it
|
||||
CHECK(prev_ref->next() == nullptr,
|
||||
"All future references should have been replaced");
|
||||
after = prev_ref->instr();
|
||||
} else {
|
||||
// there is no previous reference, insert store immediately after definition
|
||||
CHECK(interval->instr->uses().head() == nullptr,
|
||||
"All future references should have been replaced");
|
||||
after = interval->instr;
|
||||
}
|
||||
|
||||
builder.SetInsertPoint({after});
|
||||
builder.StoreLocal(local, interval->instr);
|
||||
|
||||
// since the interval that this store belongs to has now expired, there's no
|
||||
// need to assign an ordinal to it
|
||||
|
||||
// reuse the old interval
|
||||
interval->instr = instr;
|
||||
interval->reused = nullptr;
|
||||
interval->start = instr->uses().head();
|
||||
interval->end = instr->uses().tail();
|
||||
interval->next = interval->start;
|
||||
set.InsertInterval(interval);
|
||||
|
||||
// reset insert point
|
||||
builder.SetInsertPoint(insert_point);
|
||||
|
||||
num_spills++;
|
||||
|
||||
return interval->reg;
|
||||
}
|
|
@ -1,85 +1,20 @@
|
|||
#ifndef REGISTER_ALLOCATION_PASS_H
|
||||
#define REGISTER_ALLOCATION_PASS_H
|
||||
|
||||
#include <vector>
|
||||
#include "jit/backend/backend.h"
|
||||
#include "jit/ir/passes/pass_runner.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace re {
|
||||
namespace jit {
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
struct ir_s;
|
||||
struct register_def_s;
|
||||
|
||||
struct Interval {
|
||||
Instr *instr;
|
||||
Instr *reused;
|
||||
Use *start;
|
||||
Use *end;
|
||||
Use *next;
|
||||
int reg;
|
||||
};
|
||||
extern const char *ra_name;
|
||||
|
||||
class RegisterSet {
|
||||
public:
|
||||
RegisterSet(int max_registers);
|
||||
~RegisterSet();
|
||||
void ra_run(struct ir_s *ir, const struct register_def_s *registers,
|
||||
int num_registers);
|
||||
|
||||
void Clear();
|
||||
|
||||
int PopRegister();
|
||||
void PushRegister(int reg);
|
||||
|
||||
Interval *HeadInterval();
|
||||
Interval *TailInterval();
|
||||
void PopHeadInterval();
|
||||
void PopTailInterval();
|
||||
void InsertInterval(Interval *interval);
|
||||
|
||||
private:
|
||||
// free register vector
|
||||
int *free_, num_free_;
|
||||
|
||||
// intervals used by this register set, sorted in order of next use
|
||||
Interval **live_;
|
||||
int num_live_;
|
||||
};
|
||||
|
||||
class RegisterAllocationPass : public Pass {
|
||||
public:
|
||||
static constexpr const char *NAME = "ra";
|
||||
|
||||
RegisterAllocationPass(const backend::Register *registers, int num_registers);
|
||||
~RegisterAllocationPass();
|
||||
|
||||
const char *name() {
|
||||
return "ra";
|
||||
}
|
||||
|
||||
void Run(IRBuilder &builder);
|
||||
|
||||
private:
|
||||
const backend::Register *registers_;
|
||||
int num_registers_;
|
||||
|
||||
RegisterSet int_registers_;
|
||||
RegisterSet float_registers_;
|
||||
RegisterSet vector_registers_;
|
||||
|
||||
// intervals, keyed by register
|
||||
Interval *intervals_;
|
||||
|
||||
RegisterSet &GetRegisterSet(ValueType type);
|
||||
|
||||
void Reset();
|
||||
void AssignOrdinals(IRBuilder &builder);
|
||||
void ExpireOldIntervals(Instr *instr);
|
||||
int ReuseArgRegister(IRBuilder &builder, Instr *instr);
|
||||
int AllocFreeRegister(Instr *instr);
|
||||
int AllocBlockedRegister(IRBuilder &builder, Instr *instr);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -49,7 +49,7 @@ void exception_handler_remove(re_exception_handler_t *handler) {
|
|||
}
|
||||
|
||||
bool exception_handler_handle(re_exception_t *ex) {
|
||||
list_for_each_entry(&s_live_handlers, re_exception_handler_t, it, handler) {
|
||||
list_for_each_entry(handler, &s_live_handlers, re_exception_handler_t, it) {
|
||||
if (handler->cb(handler->data, ex)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <signal.h>
|
||||
#include "sys/exception_handler_linux.h"
|
||||
|
||||
using namespace re::sys;
|
||||
|
||||
static struct sigaction old_sigsegv;
|
||||
static struct sigaction old_sigill;
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <windows.h>
|
||||
#include "sys/exception_handler_win.h"
|
||||
|
||||
using namespace re::sys;
|
||||
|
||||
static void CopyStateTo(PCONTEXT src, re_thread_state_t *dst) {
|
||||
dst->rax = src->Rax;
|
||||
dst->rcx = src->Rcx;
|
||||
|
|
|
@ -17,6 +17,7 @@ typedef enum {
|
|||
ACC_NONE,
|
||||
ACC_READONLY,
|
||||
ACC_READWRITE,
|
||||
ACC_READWRITEEXEC,
|
||||
} page_access_t;
|
||||
|
||||
size_t get_page_size();
|
||||
|
|
|
@ -48,6 +48,8 @@ static int access_to_protect_flags(page_access_t access) {
|
|||
return PROT_READ;
|
||||
case ACC_READWRITE:
|
||||
return PROT_READ | PROT_WRITE;
|
||||
case ACC_READWRITEEXEC:
|
||||
return PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
default:
|
||||
return PROT_NONE;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ static DWORD access_to_protection_flags(page_access_t access) {
|
|||
return PAGE_READONLY;
|
||||
case ACC_READWRITE:
|
||||
return PAGE_READWRITE;
|
||||
case ACC_READWRITEEXEC:
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
default:
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
|
|
@ -61,19 +61,19 @@ static void win_init_joystick(window_t *win) {
|
|||
static void win_handle_paint(window_t *win) {
|
||||
rb_begin_frame(win->rb);
|
||||
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.prepaint) {
|
||||
listener->cb.prepaint(listener->data);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.paint) {
|
||||
listener->cb.paint(listener->data, win->show_main_menu);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.postpaint) {
|
||||
listener->cb.postpaint(listener->data);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ static void win_handle_paint(window_t *win) {
|
|||
}
|
||||
|
||||
static void win_handle_keydown(window_t *win, keycode_t code, int16_t value) {
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.keydown) {
|
||||
listener->cb.keydown(listener->data, code, value);
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ static void win_handle_hatdown(window_t *win, int hat, uint8_t state,
|
|||
}
|
||||
|
||||
static void win_handle_textinput(window_t *win, const char *text) {
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.textinput) {
|
||||
listener->cb.textinput(listener->data, text);
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ static void win_handle_textinput(window_t *win, const char *text) {
|
|||
}
|
||||
|
||||
static void win_handle_mousemove(window_t *win, int x, int y) {
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.mousemove) {
|
||||
listener->cb.mousemove(listener->data, x, y);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ static void win_handle_mousemove(window_t *win, int x, int y) {
|
|||
}
|
||||
|
||||
static void win_handle_close(window_t *win) {
|
||||
list_for_each_entry(&win->live_listeners, window_listener_t, it, listener) {
|
||||
list_for_each_entry(listener, &win->live_listeners, window_listener_t, it) {
|
||||
if (listener->cb.close) {
|
||||
listener->cb.close(listener->data);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ test_ldcl_stcl_sr:
|
|||
# r1 in alt bank should have been pre-decremented by 4
|
||||
mov.l .DATA_ADDR, r1
|
||||
stc r1_bank, r5
|
||||
sub r1, r5
|
||||
rts
|
||||
nop
|
||||
# REGISTER_OUT r2 13
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
#include <sstream>
|
||||
#include <gtest/gtest.h>
|
||||
#include "jit/ir/passes/dead_code_elimination_pass.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include "jit/ir/ir_reader.h"
|
||||
#include "jit/ir/ir_writer.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
static uint8_t ir_buffer[1024 * 1024];
|
||||
static char scratch_buffer[1024 * 1024];
|
||||
|
||||
TEST(DeadCodeEliminationPassTest, Sanity) {
|
||||
static const char *input =
|
||||
static const char input_str[] =
|
||||
"i32 %0 = load_context i32 0xbc\n"
|
||||
"i32 %1 = load_guest i32 %0\n"
|
||||
"i32 %2 = load_guest i32 0x8c000a10\n"
|
||||
"i32 %3 = load_guest i32 %2\n"
|
||||
"i32 %1 = load_slow i32 %0\n"
|
||||
"i32 %2 = load_slow i32 0x8c000a10\n"
|
||||
"i32 %3 = load_slow i32 %2\n"
|
||||
"i32 %4 = load_context i32 0xc0\n"
|
||||
"i32 %5 = and i32 %3, i32 %4\n"
|
||||
"store_context i32 0xb0, i32 %5\n"
|
||||
"store_guest i32 %2, i32 %5\n"
|
||||
"store_slow i32 %2, i32 %5\n"
|
||||
"i32 %6 = load_context i32 0xe4\n"
|
||||
"i32 %7 = load_guest i32 %6\n"
|
||||
"i32 %7 = load_slow i32 %6\n"
|
||||
"store_context i32 0xb4, i32 %7\n"
|
||||
"i64 %8 = load_context i32 0x18\n"
|
||||
"i32 %9 = load_context i32 0x38\n"
|
||||
|
@ -35,15 +31,15 @@ TEST(DeadCodeEliminationPassTest, Sanity) {
|
|||
"call_external i64 %8, i64 %10\n"
|
||||
"store_context i32 0x30, i32 0x8c000940\n";
|
||||
|
||||
static const char *output =
|
||||
"i32 %0 = load_guest i32 0x8c000a10\n"
|
||||
"i32 %1 = load_guest i32 %0\n"
|
||||
static const char output_str[] =
|
||||
"i32 %0 = load_slow i32 0x8c000a10\n"
|
||||
"i32 %1 = load_slow i32 %0\n"
|
||||
"i32 %2 = load_context i32 0xc0\n"
|
||||
"i32 %3 = and i32 %1, i32 %2\n"
|
||||
"store_context i32 0xb0, i32 %3\n"
|
||||
"store_guest i32 %0, i32 %3\n"
|
||||
"store_slow i32 %0, i32 %3\n"
|
||||
"i32 %4 = load_context i32 0xe4\n"
|
||||
"i32 %5 = load_guest i32 %4\n"
|
||||
"i32 %5 = load_slow i32 %4\n"
|
||||
"store_context i32 0xb4, i32 %5\n"
|
||||
"i64 %6 = load_context i32 0x18\n"
|
||||
"i32 %7 = load_context i32 0x38\n"
|
||||
|
@ -58,19 +54,25 @@ TEST(DeadCodeEliminationPassTest, Sanity) {
|
|||
"call_external i64 %6, i64 %8\n"
|
||||
"store_context i32 0x30, i32 0x8c000940\n";
|
||||
|
||||
Arena arena(4096);
|
||||
IRBuilder builder(arena);
|
||||
ir_t ir = {};
|
||||
ir.buffer = ir_buffer;
|
||||
ir.capacity = sizeof(ir_buffer);
|
||||
|
||||
IRReader reader;
|
||||
std::stringstream input_stream(input);
|
||||
reader.Parse(input_stream, builder);
|
||||
FILE *input = tmpfile();
|
||||
fwrite(input_str, 1, sizeof(input_str) - 1, input);
|
||||
rewind(input);
|
||||
bool res = ir_read(input, &ir);
|
||||
fclose(input);
|
||||
ASSERT_TRUE(res);
|
||||
|
||||
DeadCodeEliminationPass pass;
|
||||
pass.Run(builder);
|
||||
dce_run(&ir);
|
||||
|
||||
IRWriter writer;
|
||||
std::stringstream output_stream;
|
||||
writer.Print(builder, output_stream);
|
||||
FILE *output = tmpfile();
|
||||
ir_write(&ir, output);
|
||||
rewind(output);
|
||||
size_t n = fread(&scratch_buffer, 1, sizeof(scratch_buffer), output);
|
||||
fclose(output);
|
||||
ASSERT_NE(n, 0u);
|
||||
|
||||
ASSERT_STREQ(output_stream.str().c_str(), output);
|
||||
ASSERT_STREQ(scratch_buffer, output_str);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ static void validate_people(list_t *people_list, person_t **expected_people,
|
|||
int num_expected_people) {
|
||||
int n = 0;
|
||||
|
||||
list_for_each_entry(people_list, person_t, it, person) {
|
||||
list_for_each_entry(person, people_list, person_t, it) {
|
||||
person_t *expected_person = expected_people[n];
|
||||
ASSERT_STREQ(person->name, expected_person->name);
|
||||
n++;
|
||||
|
@ -52,7 +52,7 @@ static void validate_people_reverse(list_t *people_list,
|
|||
int num_expected_people) {
|
||||
int n = 0;
|
||||
|
||||
list_for_each_entry_reverse(people_list, person_t, it, person) {
|
||||
list_for_each_entry_reverse(person, people_list, person_t, it) {
|
||||
person_t *expected_person = expected_people[num_expected_people - n - 1];
|
||||
ASSERT_STREQ(person->name, expected_person->name);
|
||||
n++;
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
#include <sstream>
|
||||
#include <gtest/gtest.h>
|
||||
#include "jit/ir/passes/load_store_elimination_pass.h"
|
||||
#include "jit/ir/ir_builder.h"
|
||||
#include "jit/ir/ir_reader.h"
|
||||
#include "jit/ir/ir_writer.h"
|
||||
#include "jit/ir/ir.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::jit::ir;
|
||||
using namespace re::jit::ir::passes;
|
||||
static uint8_t ir_buffer[1024 * 1024];
|
||||
static char scratch_buffer[1024 * 1024];
|
||||
|
||||
TEST(LoadStoreEliminationPassTest, Aliasing) {
|
||||
static const char *input =
|
||||
static const char input_str[] =
|
||||
"store_context i32 0x104, i32 0x0\n"
|
||||
"store_context i32 0x100, i32 0x0\n"
|
||||
"store_context i32 0x10c, i32 0x0\n"
|
||||
|
@ -37,7 +33,7 @@ TEST(LoadStoreEliminationPassTest, Aliasing) {
|
|||
"i32 %10 = sub i32 %9, i32 0x10\n"
|
||||
"store_context i32 0x20, i32 %10\n";
|
||||
|
||||
static const char *output =
|
||||
static const char output_str[] =
|
||||
"store_context i32 0x104, i32 0x0\n"
|
||||
"store_context i32 0x100, i32 0x0\n"
|
||||
"store_context i32 0x10c, i32 0x0\n"
|
||||
|
@ -59,19 +55,25 @@ TEST(LoadStoreEliminationPassTest, Aliasing) {
|
|||
"i32 %5 = sub i32 %4, i32 0x10\n"
|
||||
"store_context i32 0x20, i32 %5\n";
|
||||
|
||||
Arena arena(4096);
|
||||
IRBuilder builder(arena);
|
||||
ir_t ir = {};
|
||||
ir.buffer = ir_buffer;
|
||||
ir.capacity = sizeof(ir_buffer);
|
||||
|
||||
IRReader reader;
|
||||
std::stringstream input_stream(input);
|
||||
reader.Parse(input_stream, builder);
|
||||
FILE *input = tmpfile();
|
||||
fwrite(input_str, 1, sizeof(input_str) - 1, input);
|
||||
rewind(input);
|
||||
bool res = ir_read(input, &ir);
|
||||
fclose(input);
|
||||
ASSERT_TRUE(res);
|
||||
|
||||
LoadStoreEliminationPass pass;
|
||||
pass.Run(builder);
|
||||
lse_run(&ir);
|
||||
|
||||
IRWriter writer;
|
||||
std::stringstream output_stream;
|
||||
writer.Print(builder, output_stream);
|
||||
FILE *output = tmpfile();
|
||||
ir_write(&ir, output);
|
||||
rewind(output);
|
||||
size_t n = fread(&scratch_buffer, 1, sizeof(scratch_buffer), output);
|
||||
fclose(output);
|
||||
ASSERT_NE(n, 0u);
|
||||
|
||||
ASSERT_STREQ(output_stream.str().c_str(), output);
|
||||
ASSERT_STREQ(scratch_buffer, output_str);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <unordered_map>
|
||||
#include <gtest/gtest.h>
|
||||
#include "core/math.h"
|
||||
#include "core/memory.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
|
@ -146,14 +145,15 @@ void run_sh4_test(const SH4Test &test) {
|
|||
for (int i = 0; i < sh4_num_test_regs; i++) {
|
||||
SH4TestRegister ® = sh4_test_regs[i];
|
||||
|
||||
uint32_t input = load<uint32_t>(
|
||||
uint32_t input = *reinterpret_cast<const uint32_t *>(
|
||||
reinterpret_cast<const uint8_t *>(&test.in) + reg.offset);
|
||||
|
||||
if (input == UNINITIALIZED_REG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
store(reinterpret_cast<uint8_t *>(&dc->sh4->ctx) + reg.offset, input);
|
||||
*reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(&dc->sh4->ctx) +
|
||||
reg.offset) = input;
|
||||
}
|
||||
|
||||
// setup initial stack pointer
|
||||
|
@ -178,14 +178,14 @@ void run_sh4_test(const SH4Test &test) {
|
|||
for (int i = 0; i < sh4_num_test_regs; i++) {
|
||||
SH4TestRegister ® = sh4_test_regs[i];
|
||||
|
||||
uint32_t expected = load<uint32_t>(
|
||||
uint32_t expected = *reinterpret_cast<const uint32_t *>(
|
||||
reinterpret_cast<const uint8_t *>(&test.out) + reg.offset);
|
||||
|
||||
if (expected == UNINITIALIZED_REG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t actual = load<uint32_t>(
|
||||
uint32_t actual = *reinterpret_cast<const uint32_t *>(
|
||||
reinterpret_cast<const uint8_t *>(&dc->sh4->ctx) + reg.offset);
|
||||
|
||||
ASSERT_EQ(expected, actual) << reg.name << " expected: 0x" << std::hex
|
||||
|
|
|
@ -112,13 +112,13 @@ TEST_SH4(test_ldc_stc_sr,(uint8_t *)"\x0d\xe2\x1b\xd0\x0e\x40\x63\xe2\x02\x01\x1
|
|||
TEST_SH4(test_ldc_stc_rbank,(uint8_t *)"\x0d\xe2\x1b\xd0\x0e\x40\x63\xe2\x02\x01\x15\xd0\x12\x20\x1c\xd0\x0e\x40\x13\xd0\x02\x63\x0b\x00\x09\x00\x9e\x40\x63\xe1\x92\x01\x0b\x00\x09\x00\x1e\x40\x12\x01\x0b\x00\x09\x00\x2e\x40\x22\x01\x0b\x00\x09\x00\x3e\x40\x32\x01\x0b\x00\x09\x00\x4e\x40\x42\x01\x0b\x00\x09\x00\xfa\x40\xfa\x01\x0b\x00\x09\x00\x09\x00\x09\x00\x00\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x70\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",144,0x1a,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldc_stc_ssr,(uint8_t *)"\x0d\xe2\x1b\xd0\x0e\x40\x63\xe2\x02\x01\x15\xd0\x12\x20\x1c\xd0\x0e\x40\x13\xd0\x02\x63\x0b\x00\x09\x00\x9e\x40\x63\xe1\x92\x01\x0b\x00\x09\x00\x1e\x40\x12\x01\x0b\x00\x09\x00\x2e\x40\x22\x01\x0b\x00\x09\x00\x3e\x40\x32\x01\x0b\x00\x09\x00\x4e\x40\x42\x01\x0b\x00\x09\x00\xfa\x40\xfa\x01\x0b\x00\x09\x00\x09\x00\x09\x00\x00\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x70\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",144,0x34,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldc_stc_dbr,(uint8_t *)"\x0d\xe2\x1b\xd0\x0e\x40\x63\xe2\x02\x01\x15\xd0\x12\x20\x1c\xd0\x0e\x40\x13\xd0\x02\x63\x0b\x00\x09\x00\x9e\x40\x63\xe1\x92\x01\x0b\x00\x09\x00\x1e\x40\x12\x01\x0b\x00\x09\x00\x2e\x40\x22\x01\x0b\x00\x09\x00\x3e\x40\x32\x01\x0b\x00\x09\x00\x4e\x40\x42\x01\x0b\x00\x09\x00\xfa\x40\xfa\x01\x0b\x00\x09\x00\x09\x00\x09\x00\x00\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x70\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",144,0x44,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_spc,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x80,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_sr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x0,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0x500000f0,0x4,0x0,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_rbank,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x26,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_vbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x54,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_gbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x3e,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_ssr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x6a,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_dbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x18\x35\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1a\xd1\x02\x21\x19\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0f\xd1\x02\x21\x0e\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x96,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_spc,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x7e,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_sr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x0,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0x500000f0,0x4,0x0,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_rbank,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x24,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_vbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x52,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_gbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x3c,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_ssr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x68,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_ldcl_stcl_dbr,(uint8_t *)"\x0d\xe2\x33\xd0\x07\x40\x63\xe2\x2d\xd1\x04\x71\x03\x41\x34\xd3\x07\x43\x2b\xd0\x02\x63\x2e\xd1\x82\x04\x18\x34\x28\xd1\x92\x05\x0b\x00\x09\x00\x26\xd1\x02\x21\xb7\x41\x63\xe3\x24\xd2\x08\x72\xb3\x42\x10\x32\x29\x04\x22\x65\x0b\x00\x09\x00\x20\xd1\x02\x21\x1f\xd2\x08\x72\x17\x41\x13\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x1b\xd1\x02\x21\x1a\xd2\x08\x72\x27\x41\x23\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x15\xd1\x02\x21\x14\xd2\x08\x72\x37\x41\x33\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x10\xd1\x02\x21\x0f\xd2\x08\x72\x47\x41\x43\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x0a\xd1\x02\x21\x09\xd2\x08\x72\xf6\x41\xf2\x42\x20\x31\x29\x03\x22\x64\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\xf0\x00\x00\x50\xf0\x00\x00\x70\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb0\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xb4\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00",240,0x94,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x1,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_lds_sts_mach,(uint8_t *)"\x0a\x40\x0a\x01\x0b\x00\x09\x00\x1a\x40\x1a\x01\x0b\x00\x09\x00\x2a\x02\x2a\x40\x2a\x01\x2a\x42\x0b\x00\x09\x00\x6a\x40\x6a\x01\x0b\x00\x09\x00\x5a\x40\x5a\x01\x0b\x00\x09\x00",44,0x0,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_lds_sts_fpul,(uint8_t *)"\x0a\x40\x0a\x01\x0b\x00\x09\x00\x1a\x40\x1a\x01\x0b\x00\x09\x00\x2a\x02\x2a\x40\x2a\x01\x2a\x42\x0b\x00\x09\x00\x6a\x40\x6a\x01\x0b\x00\x09\x00\x5a\x40\x5a\x01\x0b\x00\x09\x00",44,0x24,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
TEST_SH4(test_lds_sts_macl,(uint8_t *)"\x0a\x40\x0a\x01\x0b\x00\x09\x00\x1a\x40\x1a\x01\x0b\x00\x09\x00\x2a\x02\x2a\x40\x2a\x01\x2a\x42\x0b\x00\x09\x00\x6a\x40\x6a\x01\x0b\x00\x09\x00\x5a\x40\x5a\x01\x0b\x00\x09\x00",44,0x8,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xd,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d)
|
||||
|
|
Loading…
Reference in New Issue