make Instr inherit from value, removing result property

updated register allocation to account for the fact that the result is no longer a part of the reference set
added basic dead code elimination pass
This commit is contained in:
Anthony Pesch 2016-03-21 23:39:13 -07:00
parent f936b47b2b
commit d234f630f1
26 changed files with 598 additions and 643 deletions

View File

@ -162,10 +162,10 @@ set(REDREAM_SOURCES
src/jit/ir/ir_reader.cc
src/jit/ir/ir_writer.cc
src/jit/ir/passes/constant_propagation_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/register_allocation_pass.cc
src/jit/ir/passes/validate_pass.cc
src/renderer/gl_backend.cc
src/renderer/gl_shader.cc
src/sys/exception_handler.cc
@ -336,6 +336,7 @@ set(REDREAM_TEST_SOURCES
${REDREAM_SOURCES}
test/test_interval_tree.cc
test/test_intrusive_list.cc
test/test_dead_code_elimination_pass.cc
test/test_load_store_elimination_pass.cc
test/test_minmax_heap.cc
test/test_sh4.cc

View File

@ -7,22 +7,19 @@
namespace re {
template <typename T>
class array
{
class array {
public:
array(size_t size = 8) : data_(nullptr), size_(0), capacity_(0) {
Resize(size);
}
~array() {
free(data_);
}
~array() { free(data_); }
array(array const &) = delete;
void operator=(array const &) = delete;
T &operator[] (size_t i) { return data_[i]; }
T operator[] (size_t i) const { return data_[i]; }
T &operator[](size_t i) { return data_[i]; }
T operator[](size_t i) const { return data_[i]; }
T *data() { return data_; }
const T *data() const { return data_; }
@ -60,12 +57,11 @@ class array
void PopBack() { size_--; }
private:
private:
T *data_;
size_t size_;
size_t capacity_;
};
}
#endif

View File

@ -11,7 +11,7 @@
#define ANSI_COLOR_RESET "\x1b[0m"
void Log(LogLevel level, const char *format, ...) {
static char buffer[1024];
static char buffer[10240];
va_list args;
va_start(args, format);

View File

@ -6,9 +6,9 @@
#include "jit/frontend/sh4/sh4_frontend.h"
#include "jit/ir/ir_builder.h"
#include "jit/ir/passes/constant_propagation_pass.h"
#include "jit/ir/passes/dead_code_elimination_pass.h"
#include "jit/ir/passes/load_store_elimination_pass.h"
#include "jit/ir/passes/register_allocation_pass.h"
#include "jit/ir/passes/validate_pass.h"
using namespace re::hw;
using namespace re::jit;
@ -41,9 +41,9 @@ SH4CodeCache::SH4CodeCache(Memory *memory, void *guest_ctx,
}
// setup optimization passes
pass_runner_.AddPass(std::unique_ptr<Pass>(new ValidatePass()));
pass_runner_.AddPass(std::unique_ptr<Pass>(new LoadStoreEliminationPass()));
pass_runner_.AddPass(std::unique_ptr<Pass>(new ConstantPropagationPass()));
pass_runner_.AddPass(std::unique_ptr<Pass>(new DeadCodeEliminationPass()));
pass_runner_.AddPass(
std::unique_ptr<Pass>(new RegisterAllocationPass(*backend_)));
@ -90,7 +90,7 @@ SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t addr, int max_instrs) {
// compile the SH4 into IR
std::unique_ptr<IRBuilder> builder = frontend_->BuildBlock(addr, max_instrs);
pass_runner_.Run(*builder);
pass_runner_.Run(*builder, false);
// assemble the IR into native code
BlockPointer run = backend_->AssembleBlock(*builder, block->flags);

View File

@ -137,12 +137,7 @@ void X64Emitter::EmitProlog(IRBuilder &builder, int *out_stack_size) {
modified_marker_++;
for (auto instr : builder.instrs()) {
Value *result = instr->result();
if (!result) {
continue;
}
int i = result->reg();
int i = instr->reg();
if (i == NO_REGISTER) {
continue;
}
@ -345,10 +340,10 @@ bool X64Emitter::CanEncodeAsImmediate(const Value *v) const {
EMITTER(LOAD_HOST) {
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_F32:
e.vmovss(result, e.dword[a]);
break;
@ -360,9 +355,9 @@ EMITTER(LOAD_HOST) {
break;
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
e.mov(result, e.byte[a]);
break;
@ -423,7 +418,7 @@ EMITTER(STORE_HOST) {
}
EMITTER(LOAD_GUEST) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (instr->arg0()->constant()) {
// try to resolve the address to a physical page
@ -442,7 +437,7 @@ EMITTER(LOAD_GUEST) {
// we didn't have to store the absolute address in the scratch register
e.mov(e.rax, reinterpret_cast<uint64_t>(host_addr));
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
e.mov(result, e.byte[e.rax]);
break;
@ -468,7 +463,7 @@ EMITTER(LOAD_GUEST) {
if (e.block_flags() & BF_SLOWMEM) {
void *fn = nullptr;
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
fn = reinterpret_cast<void *>(
static_cast<uint8_t (*)(Memory *, uint32_t)>(&Memory::R8));
@ -499,7 +494,7 @@ EMITTER(LOAD_GUEST) {
e.mov(e.r10, reinterpret_cast<uint64_t>(e.guest_ctx()));
e.mov(e.r11, reinterpret_cast<uint64_t>(e.memory()->protected_base()));
} else {
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
e.mov(result, e.byte[a.cvt64() + e.r11]);
break;
@ -618,10 +613,10 @@ EMITTER(STORE_GUEST) {
EMITTER(LOAD_CONTEXT) {
int offset = instr->arg0()->i32();
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_F32:
e.movss(result, e.dword[e.r10 + offset]);
break;
@ -633,9 +628,9 @@ EMITTER(LOAD_CONTEXT) {
break;
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
e.mov(result, e.byte[e.r10 + offset]);
break;
@ -720,10 +715,10 @@ EMITTER(STORE_CONTEXT) {
EMITTER(LOAD_LOCAL) {
int offset = instr->arg0()->i32();
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_F32:
e.movss(result, e.dword[e.rsp + offset]);
break;
@ -735,9 +730,9 @@ EMITTER(LOAD_LOCAL) {
break;
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I8:
e.mov(result, e.byte[e.rsp + offset]);
break;
@ -800,7 +795,7 @@ EMITTER(STORE_LOCAL) {
}
EMITTER(BITCAST) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result.getIdx() == a.getIdx()) {
@ -812,11 +807,11 @@ EMITTER(BITCAST) {
}
EMITTER(CAST) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_F32:
CHECK_EQ(instr->arg0()->type(), VALUE_I32);
e.cvtsi2ss(result, a);
@ -830,10 +825,10 @@ EMITTER(CAST) {
break;
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
switch (instr->result()->type()) {
switch (instr->type()) {
case VALUE_I32:
CHECK_EQ(instr->arg0()->type(), VALUE_F32);
e.cvttss2si(result, a);
@ -850,7 +845,7 @@ EMITTER(CAST) {
}
EMITTER(SEXT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (a == result) {
@ -866,7 +861,7 @@ EMITTER(SEXT) {
}
EMITTER(ZEXT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (a == result) {
@ -883,7 +878,7 @@ EMITTER(ZEXT) {
}
EMITTER(SELECT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg cond = e.GetRegister(instr->arg0());
const Xbyak::Reg a = e.GetRegister(instr->arg1());
const Xbyak::Reg b = e.GetRegister(instr->arg2());
@ -894,7 +889,7 @@ EMITTER(SELECT) {
}
EMITTER(EQ) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -920,7 +915,7 @@ EMITTER(EQ) {
}
EMITTER(NE) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -946,7 +941,7 @@ EMITTER(NE) {
}
EMITTER(SGE) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -974,7 +969,7 @@ EMITTER(SGE) {
}
EMITTER(SGT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -1002,7 +997,7 @@ EMITTER(SGT) {
}
EMITTER(UGE) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (e.CanEncodeAsImmediate(instr->arg1())) {
@ -1016,7 +1011,7 @@ EMITTER(UGE) {
}
EMITTER(UGT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (e.CanEncodeAsImmediate(instr->arg1())) {
@ -1030,7 +1025,7 @@ EMITTER(UGT) {
}
EMITTER(SLE) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -1058,7 +1053,7 @@ EMITTER(SLE) {
}
EMITTER(SLT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
if (IsFloatType(instr->arg0()->type())) {
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
@ -1086,7 +1081,7 @@ EMITTER(SLT) {
}
EMITTER(ULE) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (e.CanEncodeAsImmediate(instr->arg1())) {
@ -1100,7 +1095,7 @@ EMITTER(ULE) {
}
EMITTER(ULT) {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (e.CanEncodeAsImmediate(instr->arg1())) {
@ -1114,12 +1109,12 @@ EMITTER(ULT) {
}
EMITTER(ADD) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
const Xbyak::Xmm b = e.GetXMMRegister(instr->arg1());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
if (result != a) {
e.movss(result, a);
}
@ -1133,7 +1128,7 @@ EMITTER(ADD) {
e.addsd(result, b);
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1150,12 +1145,12 @@ EMITTER(ADD) {
}
EMITTER(SUB) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
const Xbyak::Xmm b = e.GetXMMRegister(instr->arg1());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
if (result != a) {
e.movss(result, a);
}
@ -1169,7 +1164,7 @@ EMITTER(SUB) {
e.subsd(result, b);
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1186,12 +1181,12 @@ EMITTER(SUB) {
}
EMITTER(SMUL) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
const Xbyak::Xmm b = e.GetXMMRegister(instr->arg1());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
if (result != a) {
e.movss(result, a);
}
@ -1205,7 +1200,7 @@ EMITTER(SMUL) {
e.mulsd(result, b);
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
const Xbyak::Reg b = e.GetRegister(instr->arg1());
@ -1218,9 +1213,9 @@ EMITTER(SMUL) {
}
EMITTER(UMUL) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
const Xbyak::Reg b = e.GetRegister(instr->arg1());
@ -1232,13 +1227,13 @@ EMITTER(UMUL) {
}
EMITTER(DIV) {
CHECK(IsFloatType(instr->result()->type()));
CHECK(IsFloatType(instr->type()));
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
const Xbyak::Xmm b = e.GetXMMRegister(instr->arg1());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
if (result != a) {
e.movss(result, a);
}
@ -1254,11 +1249,11 @@ EMITTER(DIV) {
}
EMITTER(NEG) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
// TODO use xorps
e.movd(e.eax, a);
e.xor (e.eax, (uint32_t)0x80000000);
@ -1271,7 +1266,7 @@ EMITTER(NEG) {
e.movq(result, e.rax);
}
} else {
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1283,12 +1278,12 @@ EMITTER(NEG) {
}
EMITTER(SQRT) {
CHECK(IsFloatType(instr->result()->type()));
CHECK(IsFloatType(instr->type()));
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
e.sqrtss(result, a);
} else {
e.sqrtsd(result, a);
@ -1296,11 +1291,11 @@ EMITTER(SQRT) {
}
EMITTER(ABS) {
if (IsFloatType(instr->result()->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr->result());
if (IsFloatType(instr->type())) {
const Xbyak::Xmm result = e.GetXMMRegister(instr);
const Xbyak::Xmm a = e.GetXMMRegister(instr->arg0());
if (instr->result()->type() == VALUE_F32) {
if (instr->type() == VALUE_F32) {
// TODO use andps
e.movd(e.eax, a);
e.and (e.eax, (uint32_t)0x7fffffff);
@ -1321,9 +1316,9 @@ EMITTER(ABS) {
}
EMITTER(AND) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1339,9 +1334,9 @@ EMITTER(AND) {
}
EMITTER(OR) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1357,9 +1352,9 @@ EMITTER(OR) {
}
EMITTER(XOR) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1375,9 +1370,9 @@ EMITTER(XOR) {
}
EMITTER(NOT) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1388,9 +1383,9 @@ EMITTER(NOT) {
}
EMITTER(SHL) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1407,9 +1402,9 @@ EMITTER(SHL) {
}
EMITTER(ASHR) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1426,9 +1421,9 @@ EMITTER(ASHR) {
}
EMITTER(LSHR) {
CHECK(IsIntType(instr->result()->type()));
CHECK(IsIntType(instr->type()));
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg a = e.GetRegister(instr->arg0());
if (result != a) {
@ -1445,9 +1440,9 @@ EMITTER(LSHR) {
}
EMITTER(ASHD) {
CHECK_EQ(instr->result()->type(), VALUE_I32);
CHECK_EQ(instr->type(), VALUE_I32);
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg v = e.GetRegister(instr->arg0());
const Xbyak::Reg n = e.GetRegister(instr->arg1());
@ -1486,9 +1481,9 @@ EMITTER(ASHD) {
}
EMITTER(LSHD) {
CHECK_EQ(instr->result()->type(), VALUE_I32);
CHECK_EQ(instr->type(), VALUE_I32);
const Xbyak::Reg result = e.GetRegister(instr->result());
const Xbyak::Reg result = e.GetRegister(instr);
const Xbyak::Reg v = e.GetRegister(instr->arg0());
const Xbyak::Reg n = e.GetRegister(instr->arg1());

View File

@ -38,9 +38,9 @@ uint64_t Value::GetZExtValue() const {
}
}
void Value::AddRef(ValueRef *ref) { refs_.Append(ref); }
void Value::AddRef(Use *ref) { refs_.Append(ref); }
void Value::RemoveRef(ValueRef *ref) { refs_.Remove(ref); }
void Value::RemoveRef(Use *ref) { refs_.Remove(ref); }
void Value::ReplaceRefsWith(Value *other) {
CHECK_NE(this, other);
@ -48,25 +48,32 @@ void Value::ReplaceRefsWith(Value *other) {
// NOTE set_value will modify refs, be careful iterating
auto it = refs_.begin();
while (it != refs_.end()) {
ValueRef *ref = *(it++);
Use *ref = *(it++);
ref->set_value(other);
}
}
ValueRef::ValueRef(Instr *instr) : instr_(instr), value_(nullptr) {}
//
// Use
//
Use::Use(Instr *instr) : instr_(instr), value_(nullptr) {}
ValueRef::~ValueRef() {
Use::~Use() {
if (value_) {
value_->RemoveRef(this);
}
}
//
// Local
//
Local::Local(ValueType ty, Value *offset) : type_(ty), offset_(offset) {}
//
// Instr
//
Instr::Instr(Op op) : op_(op), args_{{this}, {this}, {this}, {this}}, tag_(0) {}
Instr::Instr(Op op, ValueType result_type)
: Value(result_type), op_(op), uses_{{this}, {this}, {this}}, tag_(0) {}
Instr::~Instr() {}
@ -79,7 +86,7 @@ void IRBuilder::Dump() const {
IRWriter writer;
std::ostringstream ss;
writer.Print(*this, ss);
LOG_INFO(ss.str().c_str());
LOG_INFO("%s", ss.str().c_str());
}
InsertPoint IRBuilder::GetInsertPoint() { return {current_instr_}; }
@ -95,14 +102,12 @@ void IRBuilder::RemoveInstr(Instr *instr) {
instr->~Instr();
}
Value *IRBuilder::LoadHost(Value *addr, ValueType type) {
Instr *IRBuilder::LoadHost(Value *addr, ValueType type) {
CHECK_EQ(VALUE_I64, addr->type());
Instr *instr = AppendInstr(OP_LOAD_HOST);
Value *result = AllocDynamic(type);
Instr *instr = AppendInstr(OP_LOAD_HOST, type);
instr->set_arg0(addr);
instr->set_result(result);
return result;
return instr;
}
void IRBuilder::StoreHost(Value *addr, Value *v) {
@ -113,14 +118,12 @@ void IRBuilder::StoreHost(Value *addr, Value *v) {
instr->set_arg1(v);
}
Value *IRBuilder::LoadGuest(Value *addr, ValueType type) {
Instr *IRBuilder::LoadGuest(Value *addr, ValueType type) {
CHECK_EQ(VALUE_I32, addr->type());
Instr *instr = AppendInstr(OP_LOAD_GUEST);
Value *result = AllocDynamic(type);
Instr *instr = AppendInstr(OP_LOAD_GUEST, type);
instr->set_arg0(addr);
instr->set_result(result);
return result;
return instr;
}
void IRBuilder::StoreGuest(Value *addr, Value *v) {
@ -131,12 +134,10 @@ void IRBuilder::StoreGuest(Value *addr, Value *v) {
instr->set_arg1(v);
}
Value *IRBuilder::LoadContext(size_t offset, ValueType type) {
Instr *instr = AppendInstr(OP_LOAD_CONTEXT);
Value *result = AllocDynamic(type);
Instr *IRBuilder::LoadContext(size_t offset, ValueType type) {
Instr *instr = AppendInstr(OP_LOAD_CONTEXT, type);
instr->set_arg0(AllocConstant((int32_t)offset));
instr->set_result(result);
return result;
return instr;
}
void IRBuilder::StoreContext(size_t offset, Value *v) {
@ -145,12 +146,10 @@ void IRBuilder::StoreContext(size_t offset, Value *v) {
instr->set_arg1(v);
}
Value *IRBuilder::LoadLocal(Local *local) {
Instr *instr = AppendInstr(OP_LOAD_LOCAL);
Value *result = AllocDynamic(local->type());
Instr *IRBuilder::LoadLocal(Local *local) {
Instr *instr = AppendInstr(OP_LOAD_LOCAL, local->type());
instr->set_arg0(local->offset());
instr->set_result(result);
return result;
return instr;
}
void IRBuilder::StoreLocal(Local *local, Value *v) {
@ -159,366 +158,302 @@ void IRBuilder::StoreLocal(Local *local, Value *v) {
instr->set_arg1(v);
}
Value *IRBuilder::Bitcast(Value *v, ValueType dest_type) {
Instr *IRBuilder::Bitcast(Value *v, ValueType dest_type) {
CHECK((IsIntType(v->type()) && IsIntType(dest_type)) ||
(IsFloatType(v->type()) && IsFloatType(dest_type)));
Instr *instr = AppendInstr(OP_BITCAST);
Value *result = AllocDynamic(dest_type);
Instr *instr = AppendInstr(OP_BITCAST, dest_type);
instr->set_arg0(v);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Cast(Value *v, ValueType dest_type) {
Instr *IRBuilder::Cast(Value *v, ValueType dest_type) {
CHECK((IsIntType(v->type()) && IsFloatType(dest_type)) ||
(IsFloatType(v->type()) && IsIntType(dest_type)));
Instr *instr = AppendInstr(OP_CAST);
Value *result = AllocDynamic(dest_type);
Instr *instr = AppendInstr(OP_CAST, dest_type);
instr->set_arg0(v);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SExt(Value *v, ValueType dest_type) {
Instr *IRBuilder::SExt(Value *v, ValueType dest_type) {
CHECK(IsIntType(v->type()) && IsIntType(dest_type));
Instr *instr = AppendInstr(OP_SEXT);
Value *result = AllocDynamic(dest_type);
Instr *instr = AppendInstr(OP_SEXT, dest_type);
instr->set_arg0(v);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::ZExt(Value *v, ValueType dest_type) {
Instr *IRBuilder::ZExt(Value *v, ValueType dest_type) {
CHECK(IsIntType(v->type()) && IsIntType(dest_type));
Instr *instr = AppendInstr(OP_ZEXT);
Value *result = AllocDynamic(dest_type);
Instr *instr = AppendInstr(OP_ZEXT, dest_type);
instr->set_arg0(v);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Select(Value *cond, Value *t, Value *f) {
Instr *IRBuilder::Select(Value *cond, Value *t, Value *f) {
CHECK_EQ(t->type(), f->type());
if (cond->type() != VALUE_I8) {
cond = NE(cond, AllocConstant(0));
}
Instr *instr = AppendInstr(OP_SELECT);
Value *result = AllocDynamic(t->type());
Instr *instr = AppendInstr(OP_SELECT, t->type());
instr->set_arg0(cond);
instr->set_arg1(t);
instr->set_arg2(f);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::EQ(Value *a, Value *b) {
Instr *IRBuilder::EQ(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_EQ);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_EQ, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::NE(Value *a, Value *b) {
Instr *IRBuilder::NE(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_NE);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_NE, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SGE(Value *a, Value *b) {
Instr *IRBuilder::SGE(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SGE);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_SGE, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SGT(Value *a, Value *b) {
Instr *IRBuilder::SGT(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SGT);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_SGT, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::UGE(Value *a, Value *b) {
Instr *IRBuilder::UGE(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
CHECK_EQ(true, IsIntType(a->type()) && IsIntType(b->type()));
Instr *instr = AppendInstr(OP_UGE);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_UGE, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::UGT(Value *a, Value *b) {
Instr *IRBuilder::UGT(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
CHECK_EQ(true, IsIntType(a->type()) && IsIntType(b->type()));
Instr *instr = AppendInstr(OP_UGT);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_UGT, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SLE(Value *a, Value *b) {
Instr *IRBuilder::SLE(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SLE);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_SLE, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SLT(Value *a, Value *b) {
Instr *IRBuilder::SLT(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SLT);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_SLT, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::ULE(Value *a, Value *b) {
Instr *IRBuilder::ULE(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
CHECK_EQ(true, IsIntType(a->type()) && IsIntType(b->type()));
Instr *instr = AppendInstr(OP_ULE);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_ULE, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::ULT(Value *a, Value *b) {
Instr *IRBuilder::ULT(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
CHECK_EQ(true, IsIntType(a->type()) && IsIntType(b->type()));
Instr *instr = AppendInstr(OP_ULT);
Value *result = AllocDynamic(VALUE_I8);
Instr *instr = AppendInstr(OP_ULT, VALUE_I8);
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Add(Value *a, Value *b) {
Instr *IRBuilder::Add(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_ADD);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_ADD, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Sub(Value *a, Value *b) {
Instr *IRBuilder::Sub(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SUB);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_SUB, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::SMul(Value *a, Value *b) {
Instr *IRBuilder::SMul(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_SMUL);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_SMUL, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::UMul(Value *a, Value *b) {
Instr *IRBuilder::UMul(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
CHECK(IsIntType(a->type()));
Instr *instr = AppendInstr(OP_UMUL);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_UMUL, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Div(Value *a, Value *b) {
Instr *IRBuilder::Div(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_DIV);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_DIV, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Neg(Value *a) {
Instr *instr = AppendInstr(OP_NEG);
Value *result = AllocDynamic(a->type());
Instr *IRBuilder::Neg(Value *a) {
Instr *instr = AppendInstr(OP_NEG, a->type());
instr->set_arg0(a);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Sqrt(Value *a) {
Instr *instr = AppendInstr(OP_SQRT);
Value *result = AllocDynamic(a->type());
Instr *IRBuilder::Sqrt(Value *a) {
Instr *instr = AppendInstr(OP_SQRT, a->type());
instr->set_arg0(a);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Abs(Value *a) {
Instr *instr = AppendInstr(OP_ABS);
Value *result = AllocDynamic(a->type());
Instr *IRBuilder::Abs(Value *a) {
Instr *instr = AppendInstr(OP_ABS, a->type());
instr->set_arg0(a);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::And(Value *a, Value *b) {
Instr *IRBuilder::And(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_AND);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_AND, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Or(Value *a, Value *b) {
Instr *IRBuilder::Or(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_OR);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_OR, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Xor(Value *a, Value *b) {
Instr *IRBuilder::Xor(Value *a, Value *b) {
CHECK_EQ(a->type(), b->type());
Instr *instr = AppendInstr(OP_XOR);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_XOR, a->type());
instr->set_arg0(a);
instr->set_arg1(b);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Not(Value *a) {
Instr *instr = AppendInstr(OP_NOT);
Value *result = AllocDynamic(a->type());
Instr *IRBuilder::Not(Value *a) {
Instr *instr = AppendInstr(OP_NOT, a->type());
instr->set_arg0(a);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Shl(Value *a, Value *n) {
Instr *IRBuilder::Shl(Value *a, Value *n) {
CHECK_EQ(VALUE_I32, n->type());
Instr *instr = AppendInstr(OP_SHL);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_SHL, a->type());
instr->set_arg0(a);
instr->set_arg1(n);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::Shl(Value *a, int n) {
Instr *IRBuilder::Shl(Value *a, int n) {
return Shl(a, AllocConstant((int32_t)n));
}
Value *IRBuilder::AShr(Value *a, Value *n) {
Instr *IRBuilder::AShr(Value *a, Value *n) {
CHECK_EQ(VALUE_I32, n->type());
Instr *instr = AppendInstr(OP_ASHR);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_ASHR, a->type());
instr->set_arg0(a);
instr->set_arg1(n);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::AShr(Value *a, int n) {
Instr *IRBuilder::AShr(Value *a, int n) {
return AShr(a, AllocConstant((int32_t)n));
}
Value *IRBuilder::LShr(Value *a, Value *n) {
Instr *IRBuilder::LShr(Value *a, Value *n) {
CHECK_EQ(VALUE_I32, n->type());
Instr *instr = AppendInstr(OP_LSHR);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_LSHR, a->type());
instr->set_arg0(a);
instr->set_arg1(n);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::LShr(Value *a, int n) {
Instr *IRBuilder::LShr(Value *a, int n) {
return LShr(a, AllocConstant((int32_t)n));
}
Value *IRBuilder::AShd(Value *a, Value *n) {
Instr *IRBuilder::AShd(Value *a, Value *n) {
CHECK_EQ(VALUE_I32, a->type());
CHECK_EQ(VALUE_I32, n->type());
Instr *instr = AppendInstr(OP_ASHD);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_ASHD, a->type());
instr->set_arg0(a);
instr->set_arg1(n);
instr->set_result(result);
return result;
return instr;
}
Value *IRBuilder::LShd(Value *a, Value *n) {
Instr *IRBuilder::LShd(Value *a, Value *n) {
CHECK_EQ(VALUE_I32, a->type());
CHECK_EQ(VALUE_I32, n->type());
Instr *instr = AppendInstr(OP_LSHD);
Value *result = AllocDynamic(a->type());
Instr *instr = AppendInstr(OP_LSHD, a->type());
instr->set_arg0(a);
instr->set_arg1(n);
instr->set_result(result);
return result;
return instr;
}
void IRBuilder::CallExternal1(Value *addr) {
@ -600,14 +535,21 @@ Local *IRBuilder::AllocLocal(ValueType type) {
return l;
}
Instr *IRBuilder::AllocInstr(Op op) {
Instr *IRBuilder::AllocInstr(Op op, ValueType result_type) {
Instr *instr = arena_.Alloc<Instr>();
new (instr) Instr(op);
new (instr) Instr(op, result_type);
return instr;
}
Instr *IRBuilder::AppendInstr(Op op) {
Instr *instr = AllocInstr(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;

View File

@ -51,7 +51,7 @@ enum {
};
class Instr;
class ValueRef;
class Use;
static inline bool IsFloatType(ValueType type) {
return type == VALUE_F32 || type == VALUE_F64;
@ -93,6 +93,11 @@ class Value {
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_;
@ -124,8 +129,8 @@ class Value {
}
double f64() { return static_cast<const Value *>(this)->f64(); }
const IntrusiveList<ValueRef> &refs() const { return refs_; }
IntrusiveList<ValueRef> &refs() { return refs_; }
const IntrusiveList<Use> &uses() const { return refs_; }
IntrusiveList<Use> &uses() { return refs_; }
int reg() const { return reg_; }
void set_reg(int reg) { reg_ = reg; }
@ -135,8 +140,8 @@ class Value {
uint64_t GetZExtValue() const;
void AddRef(ValueRef *ref);
void RemoveRef(ValueRef *ref);
void AddRef(Use *ref);
void RemoveRef(Use *ref);
void ReplaceRefsWith(Value *other);
private:
@ -150,19 +155,19 @@ class Value {
float f32_;
double f64_;
};
IntrusiveList<ValueRef> refs_;
IntrusiveList<Use> refs_;
// initializing here so each constructor variation doesn't have to
int reg_{NO_REGISTER};
intptr_t tag_{0};
};
// ValueRef is a layer of indirection between an instruction and a values it
// uses. Values maintain a list of all of their references, making it possible
// during optimization to replace all references to a value with a new value.
class ValueRef : public IntrusiveListNode<ValueRef> {
// 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:
ValueRef(Instr *instr);
~ValueRef();
Use(Instr *instr);
~Use();
const Instr *instr() const { return instr_; }
Instr *instr() { return instr_; }
@ -227,9 +232,7 @@ struct ValueInfo<VALUE_F64> {
};
// Locals are allocated for values that need to be spilled to the stack during
// register allocation. When allocated, a default offset of 0 is assigned,
// each backend is expected to update the offset to an appropriate value
// before emitting.
// register allocation.
class Local : public IntrusiveListNode<Local> {
public:
Local(ValueType ty, Value *offset);
@ -252,9 +255,9 @@ class Local : public IntrusiveListNode<Local> {
//
// instructions
//
class Instr : public IntrusiveListNode<Instr> {
class Instr : public Value, public IntrusiveListNode<Instr> {
public:
Instr(Op op);
Instr(Op op, ValueType result_type);
~Instr();
Op op() const { return op_; }
@ -271,20 +274,25 @@ class Instr : public IntrusiveListNode<Instr> {
Value *arg2() { return arg(2); }
void set_arg2(Value *v) { set_arg(2, v); }
const Value *result() const { return arg(3); }
Value *result() { return arg(3); }
void set_result(Value *v) { set_arg(3, v); }
const Value *arg(int i) const { return args_[i].value(); }
Value *arg(int i) { return args_[i].value(); }
void set_arg(int i, Value *v) { args_[i].set_value(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_;
ValueRef args_[4];
Use uses_[3];
intptr_t tag_;
};
@ -317,63 +325,63 @@ class IRBuilder {
void RemoveInstr(Instr *instr);
// direct access to host memory
Value *LoadHost(Value *addr, ValueType type);
Instr *LoadHost(Value *addr, ValueType type);
void StoreHost(Value *addr, Value *v);
// guest memory operations
Value *LoadGuest(Value *addr, ValueType type);
Instr *LoadGuest(Value *addr, ValueType type);
void StoreGuest(Value *addr, Value *v);
// context operations
Value *LoadContext(size_t offset, ValueType type);
Instr *LoadContext(size_t offset, ValueType type);
void StoreContext(size_t offset, Value *v);
// local operations
Value *LoadLocal(Local *local);
Instr *LoadLocal(Local *local);
void StoreLocal(Local *local, Value *v);
// cast / conversion operations
Value *Bitcast(Value *v, ValueType dest_type);
Value *Cast(Value *v, ValueType dest_type);
Value *SExt(Value *v, ValueType dest_type);
Value *ZExt(Value *v, ValueType dest_type);
Instr *Bitcast(Value *v, ValueType dest_type);
Instr *Cast(Value *v, ValueType dest_type);
Instr *SExt(Value *v, ValueType dest_type);
Instr *ZExt(Value *v, ValueType dest_type);
// conditionals
Value *Select(Value *cond, Value *t, Value *f);
Value *EQ(Value *a, Value *b);
Value *NE(Value *a, Value *b);
Value *SGE(Value *a, Value *b);
Value *SGT(Value *a, Value *b);
Value *UGE(Value *a, Value *b);
Value *UGT(Value *a, Value *b);
Value *SLE(Value *a, Value *b);
Value *SLT(Value *a, Value *b);
Value *ULE(Value *a, Value *b);
Value *ULT(Value *a, Value *b);
Instr *Select(Value *cond, Value *t, Value *f);
Instr *EQ(Value *a, Value *b);
Instr *NE(Value *a, Value *b);
Instr *SGE(Value *a, Value *b);
Instr *SGT(Value *a, Value *b);
Instr *UGE(Value *a, Value *b);
Instr *UGT(Value *a, Value *b);
Instr *SLE(Value *a, Value *b);
Instr *SLT(Value *a, Value *b);
Instr *ULE(Value *a, Value *b);
Instr *ULT(Value *a, Value *b);
// math operators
Value *Add(Value *a, Value *b);
Value *Sub(Value *a, Value *b);
Value *SMul(Value *a, Value *b);
Value *UMul(Value *a, Value *b);
Value *Div(Value *a, Value *b);
Value *Neg(Value *a);
Value *Sqrt(Value *a);
Value *Abs(Value *a);
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 *Sqrt(Value *a);
Instr *Abs(Value *a);
// bitwise operations
Value *And(Value *a, Value *b);
Value *Or(Value *a, Value *b);
Value *Xor(Value *a, Value *b);
Value *Not(Value *a);
Value *Shl(Value *a, Value *n);
Value *Shl(Value *a, int n);
Value *AShr(Value *a, Value *n);
Value *AShr(Value *a, int n);
Value *LShr(Value *a, Value *n);
Value *LShr(Value *a, int n);
Value *AShd(Value *a, Value *n);
Value *LShd(Value *a, Value *n);
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);
// calls
void CallExternal1(Value *addr);
@ -394,14 +402,25 @@ class IRBuilder {
Local *AllocLocal(ValueType type);
protected:
Instr *AllocInstr(Op op);
Instr *AllocInstr(Op op, ValueType result_type);
Instr *AppendInstr(Op op);
Instr *AppendInstr(Op op, ValueType result_type);
Arena arena_;
IntrusiveList<Instr> instrs_;
IntrusiveList<Local> locals_;
Instr *current_instr_;
};
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);
}
}
}
}

View File

@ -257,11 +257,24 @@ bool IRReader::ParseOperator(IRLexer &lex, IRBuilder &builder) {
}
bool IRReader::ParseInstruction(IRLexer &lex, IRBuilder &builder) {
Value *arg[4] = {};
int slot = -1;
ValueType type = VALUE_V;
Value *arg[3] = {};
// parse result type and slot number
if (lex.tok() == TOK_TYPE) {
// parse result value
if (!ParseValue(lex, builder, &arg[3]) || !ParseOperator(lex, builder)) {
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;
}
}
@ -285,12 +298,19 @@ bool IRReader::ParseInstruction(IRLexer &lex, IRBuilder &builder) {
}
// create instruction
Instr *instr = builder.AppendInstr(op);
Instr *instr = builder.AppendInstr(op, type);
for (int i = 0; i < 4; i++) {
if (arg[i]) {
instr->set_arg(i, arg[i]);
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;

View File

@ -92,8 +92,8 @@ void IRWriter::PrintValue(const Value *value, std::ostream &output) {
void IRWriter::PrintInstruction(const Instr *instr, std::ostream &output) {
// print result value if we have one
if (instr->result()) {
PrintValue(instr->result(), output);
if (instr->type() != VALUE_V) {
PrintValue(instr, output);
output << " = ";
}
@ -119,5 +119,7 @@ void IRWriter::PrintInstruction(const Instr *instr, std::ostream &output) {
need_comma = true;
}
// output << " [tag " << instr->tag() << ", reg " << instr->reg() << "]";
output << std::endl;
}

View File

@ -55,15 +55,15 @@ int fold_masks[NUM_OPS];
#define ARG1_UNSIGNED() static_cast<typename A1::unsigned_type>(ARG1())
#define ARG2_UNSIGNED() static_cast<typename A1::unsigned_type>(ARG2())
#define RESULT(expr) \
instr->result()->ReplaceRefsWith( \
instr->ReplaceRefsWith( \
builder.AllocConstant(static_cast<typename R::signed_type>(expr))); \
builder.RemoveInstr(instr)
static FoldFn GetFoldFn(Instr *instr) {
auto it = fold_cbs.find(CALLBACK_IDX(
instr->op(), instr->result() ? (int)instr->result()->type() : VALUE_V,
instr->arg0() ? (int)instr->arg0()->type() : VALUE_V,
instr->arg1() ? (int)instr->arg1()->type() : VALUE_V));
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));
if (it == fold_cbs.end()) {
return nullptr;
}
@ -90,7 +90,7 @@ static int GetConstantSig(Instr *instr) {
return cnst_sig;
}
void ConstantPropagationPass::Run(IRBuilder &builder) {
void ConstantPropagationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("ConstantPropagationPass::Run");
auto it = builder.instrs().begin();
@ -115,7 +115,7 @@ void ConstantPropagationPass::Run(IRBuilder &builder) {
}
FOLD(SELECT, ARG0_CNST) {
instr->result()->ReplaceRefsWith(ARG0() ? instr->arg1() : instr->arg2());
instr->ReplaceRefsWith(ARG0() ? instr->arg1() : instr->arg2());
builder.RemoveInstr(instr);
}
REGISTER_FOLD(SELECT, I8, I8, I8);

View File

@ -10,9 +10,9 @@ namespace passes {
class ConstantPropagationPass : public Pass {
public:
void Run(IRBuilder &builder);
const char *name() { return "Constant Propagation Pass"; }
private:
void Run(IRBuilder &builder, bool debug);
};
}
}

View File

@ -0,0 +1,27 @@
#include "emu/profiler.h"
#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;
void DeadCodeEliminationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("DeadCodeEliminationPass::Run");
// 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);
}
}
}

View File

@ -0,0 +1,23 @@
#ifndef DEAD_CODE_ELIMINATION_PASS_H
#define DEAD_CODE_ELIMINATION_PASS_H
#include "jit/backend/backend.h"
#include "jit/ir/passes/pass_runner.h"
namespace re {
namespace jit {
namespace ir {
namespace passes {
class DeadCodeEliminationPass : public Pass {
public:
const char *name() { return "Dead Code Elimination Pass"; }
void Run(IRBuilder &builder, bool debug);
};
}
}
}
}
#endif

View File

@ -7,7 +7,7 @@ using namespace re::jit::ir::passes;
LoadStoreEliminationPass::LoadStoreEliminationPass()
: available_(nullptr), num_available_(0) {}
void LoadStoreEliminationPass::Run(IRBuilder &builder) {
void LoadStoreEliminationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("LoadStoreEliminationPass::Run");
Reset();
@ -28,14 +28,13 @@ void LoadStoreEliminationPass::Run(IRBuilder &builder) {
int offset = instr->arg0()->i32();
Value *available = GetAvailable(offset);
if (available && available->type() == instr->result()->type()) {
instr->result()->ReplaceRefsWith(available);
CHECK_EQ(instr->result(), available);
if (available && available->type() == instr->type()) {
instr->ReplaceRefsWith(available);
builder.RemoveInstr(instr);
continue;
}
SetAvailable(offset, instr->result());
SetAvailable(offset, instr);
} else if (instr->op() == OP_STORE_CONTEXT) {
int offset = instr->arg0()->i32();
@ -58,7 +57,7 @@ void LoadStoreEliminationPass::Run(IRBuilder &builder) {
if (instr->op() == OP_LOAD_CONTEXT) {
int offset = instr->arg0()->i32();
int size = SizeForType(instr->result()->type());
int size = SizeForType(instr->type());
EraseAvailable(offset, size);
} else if (instr->op() == OP_STORE_CONTEXT) {

View File

@ -17,7 +17,9 @@ class LoadStoreEliminationPass : public Pass {
public:
LoadStoreEliminationPass();
void Run(IRBuilder &builder);
const char *name() { return "Load / Store Elimination Pass"; }
void Run(IRBuilder &builder, bool debug);
private:
void Reset();

View File

@ -11,10 +11,20 @@ void PassRunner::AddPass(std::unique_ptr<Pass> pass) {
passes_.push_back(std::move(pass));
}
void PassRunner::Run(IRBuilder &builder) {
void PassRunner::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("PassRunner::Run");
if (debug) {
LOG_INFO("Original:");
builder.Dump();
}
for (auto &pass : passes_) {
pass->Run(builder);
pass->Run(builder, debug);
if (debug) {
LOG_INFO("%s:", pass->name());
builder.Dump();
}
}
}

View File

@ -13,7 +13,10 @@ namespace passes {
class Pass {
public:
virtual ~Pass() {}
virtual void Run(IRBuilder &builder) = 0;
virtual const char *name() = 0;
virtual void Run(IRBuilder &builder, bool debug) = 0;
};
class PassRunner {
@ -21,7 +24,7 @@ class PassRunner {
PassRunner();
void AddPass(std::unique_ptr<Pass> pass);
void Run(IRBuilder &builder);
void Run(IRBuilder &builder, bool debug);
private:
std::vector<std::unique_ptr<Pass>> passes_;

View File

@ -75,6 +75,8 @@ void RegisterSet::PopTailInterval() {
}
void RegisterSet::InsertInterval(Interval *interval) {
CHECK(interval->start && interval->end && interval->next);
live_[num_live_++] = interval;
re::mmheap_push(live_, live_ + num_live_, LiveIntervalSort());
}
@ -90,7 +92,7 @@ RegisterAllocationPass::RegisterAllocationPass(const Backend &backend)
RegisterAllocationPass::~RegisterAllocationPass() { delete[] intervals_; }
void RegisterAllocationPass::Run(IRBuilder &builder) {
void RegisterAllocationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("RegisterAllocationPass::Run");
Reset();
@ -98,40 +100,34 @@ void RegisterAllocationPass::Run(IRBuilder &builder) {
AssignOrdinals(builder);
for (auto instr : builder.instrs()) {
Value *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) {
if (instr->type() == VALUE_V) {
continue;
}
// sort the value's ref list
result->refs().Sort([](const ValueRef *a, const ValueRef *b) {
// sort the instruction's ref list
instr->uses().Sort([](const Use *a, const Use *b) {
return GetOrdinal(a->instr()) < GetOrdinal(b->instr());
});
// get the live range of the value
ValueRef *start = result->refs().head();
ValueRef *end = result->refs().tail();
// expire any old intervals, freeing up the registers they claimed
ExpireOldIntervals(start->instr());
ExpireOldIntervals(instr);
// first, try and reuse the register of one of the incoming arguments
int reg = ReuuseArgRegister(instr, start, end);
int reg = ReuseArgRegister(builder, instr);
if (reg == NO_REGISTER) {
// else, allocate a new register for the result
reg = AllocFreeRegister(result, start, end);
reg = AllocFreeRegister(instr);
if (reg == NO_REGISTER) {
// if a register couldn't be allocated, spill a register and try again
reg = AllocBlockedRegister(builder, result, start, end);
reg = AllocBlockedRegister(builder, instr);
CHECK_NE(reg, NO_REGISTER, "Failed to allocate register");
}
}
result->set_reg(reg);
instr->set_reg(reg);
}
}
@ -180,7 +176,7 @@ void RegisterAllocationPass::AssignOrdinals(IRBuilder &builder) {
}
}
void RegisterAllocationPass::ExpireOldIntervals(Instr *start) {
void RegisterAllocationPass::ExpireOldIntervals(Instr *instr) {
auto expire_set = [&](RegisterSet &set) {
while (true) {
Interval *interval = set.HeadInterval();
@ -190,24 +186,35 @@ void RegisterAllocationPass::ExpireOldIntervals(Instr *start) {
// intervals are sorted by their next use, once one fails to expire or
// advance, they all will
if (GetOrdinal(interval->next->instr()) >= GetOrdinal(start)) {
if (GetOrdinal(interval->next->instr()) >= GetOrdinal(instr)) {
break;
}
// remove interval from the sorted set
set.PopHeadInterval();
// if there are no other uses, free the register assigned to this
// interval
if (!interval->next->next()) {
set.PushRegister(interval->reg);
}
// if there are more uses, advance the next use and reinsert the interval
// into the correct position
else {
if (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);
}
}
};
@ -220,8 +227,7 @@ void RegisterAllocationPass::ExpireOldIntervals(Instr *start) {
// 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::ReuuseArgRegister(Instr *instr, ValueRef *start,
ValueRef *end) {
int RegisterAllocationPass::ReuseArgRegister(IRBuilder &builder, Instr *instr) {
if (!instr->arg0() || instr->arg0()->constant()) {
return NO_REGISTER;
}
@ -233,7 +239,7 @@ int RegisterAllocationPass::ReuuseArgRegister(Instr *instr, ValueRef *start,
// make sure the register can hold the result type
const Register &r = registers_[prefered];
if (!RegisterCanStore(r, instr->result()->type())) {
if (!RegisterCanStore(r, instr->type())) {
return NO_REGISTER;
}
@ -245,23 +251,18 @@ int RegisterAllocationPass::ReuuseArgRegister(Instr *instr, ValueRef *start,
}
// the argument's register is not used after the current instruction, so the
// register can be reused for the result. since the interval's current next
// use (arg0 of this instruction) and the next use of the new interval (the
// result of this instruction) share the same ordinal, the interval can be
// hijacked and overwritten without having to reinsert it into the register
// set's sorted interval list
CHECK_EQ(GetOrdinal(interval->next->instr()), GetOrdinal(start->instr()));
interval->start = start;
interval->next = start;
interval->value = instr->result();
interval->end = end;
// 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(Value *value, ValueRef *start,
ValueRef *end) {
RegisterSet &set = GetRegisterSet(value->type());
int RegisterAllocationPass::AllocFreeRegister(Instr *instr) {
RegisterSet &set = GetRegisterSet(instr->type());
// get the first free register for this value type
int reg = set.PopRegister();
@ -271,10 +272,11 @@ int RegisterAllocationPass::AllocFreeRegister(Value *value, ValueRef *start,
// add interval
Interval *interval = &intervals_[reg];
interval->value = value;
interval->start = start;
interval->end = end;
interval->next = start;
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);
@ -282,75 +284,75 @@ int RegisterAllocationPass::AllocFreeRegister(Value *value, ValueRef *start,
}
int RegisterAllocationPass::AllocBlockedRegister(IRBuilder &builder,
Value *value, ValueRef *start,
ValueRef *end) {
Instr *instr) {
InsertPoint insert_point = builder.GetInsertPoint();
RegisterSet &set = GetRegisterSet(value->type());
RegisterSet &set = GetRegisterSet(instr->type());
// spill the register who's next use is furthest away from start
Interval *interval = set.TailInterval();
set.PopTailInterval();
// find the next and prev use of the register. the interval's value needs
// to be spilled to the stack after the previous use, and filled back from
// from the stack before it's next use
ValueRef *next_ref = interval->next;
ValueRef *prev_ref = next_ref->prev();
// 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?");
CHECK(prev_ref,
"Register being spilled has no prev use, why is it already live?");
CHECK_LT(GetOrdinal(prev_ref->instr()), GetOrdinal(next_ref->instr()));
// allocate a place on the stack to spill the value
Local *local = builder.AllocLocal(interval->value->type());
Local *local = builder.AllocLocal(interval->instr->type());
// insert load before next use
builder.SetInsertPoint({next_ref->instr()->prev()});
Value *load_local = builder.LoadLocal(local);
Instr *load_instr = builder.GetInsertPoint().instr;
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->value after the next use to use the new
// 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
ValueRef *next_next_ref = next_ref->next();
next_ref->set_value(load_local);
Use *next_next_ref = next_ref->next();
next_ref->set_value(load_instr);
next_ref = next_next_ref;
}
// with all references >= next_ref using the new value, prev_ref->next
// should now be null
CHECK(!prev_ref->next(), "All future references should have been replaced");
// insert spill after prev use, note that order here is extremely important.
// interval->value's ref list has already been sorted, and when the save
// 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.
builder.SetInsertPoint({prev_ref->instr()});
builder.StoreLocal(local, interval->value);
Instr *store_instr = builder.GetInsertPoint().instr;
Instr *after = nullptr;
// since the interval that this save belongs to has now expired, there's no
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
// the new store should now be the final reference
CHECK(prev_ref->next() && prev_ref->next()->instr() == store_instr,
"Spill should be the final reference for the interval value");
// overwrite the old interval
interval->value = value;
interval->start = start;
interval->next = start;
interval->end = end;
// 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

View File

@ -11,10 +11,11 @@ namespace ir {
namespace passes {
struct Interval {
Value *value;
ValueRef *start;
ValueRef *next;
ValueRef *end;
Instr *instr;
Instr *reused;
Use *start;
Use *end;
Use *next;
int reg;
};
@ -48,7 +49,9 @@ class RegisterAllocationPass : public Pass {
RegisterAllocationPass(const backend::Backend &backend);
~RegisterAllocationPass();
void Run(IRBuilder &builder);
const char *name() { return "Register Allocation Pass"; }
void Run(IRBuilder &builder, bool debug);
private:
const backend::Register *registers_;
@ -64,11 +67,10 @@ class RegisterAllocationPass : public Pass {
void Reset();
void AssignOrdinals(IRBuilder &builder);
void ExpireOldIntervals(Instr *start);
int ReuuseArgRegister(Instr *instr, ValueRef *start, ValueRef *end);
int AllocFreeRegister(Value *value, ValueRef *start, ValueRef *end);
int AllocBlockedRegister(IRBuilder &builder, Value *value, ValueRef *start,
ValueRef *end);
void ExpireOldIntervals(Instr *instr);
int ReuseArgRegister(IRBuilder &builder, Instr *instr);
int AllocFreeRegister(Instr *instr);
int AllocBlockedRegister(IRBuilder &builder, Instr *instr);
};
}
}

View File

@ -1,9 +0,0 @@
#include "emu/profiler.h"
#include "jit/ir/passes/validate_pass.h"
using namespace re::jit::ir;
using namespace re::jit::ir::passes;
void ValidatePass::Run(IRBuilder &builder) {
PROFILER_RUNTIME("ValidatePass::Run");
}

View File

@ -1,20 +0,0 @@
#ifndef VALIDATE_BLOCK_PASS_H
#define VALIDATE_BLOCK_PASS_H
#include "jit/ir/passes/pass_runner.h"
namespace re {
namespace jit {
namespace ir {
namespace passes {
class ValidatePass : public Pass {
public:
void Run(IRBuilder &builder);
};
}
}
}
}
#endif

View File

@ -1,118 +1,3 @@
test_cmpeq_imm:
# REGISTER_IN r0 13
cmp/eq #13, r0
movt r1
cmp/eq #17, r0
movt r2
rts
nop
# REGISTER_OUT r1 1
# REGISTER_OUT r2 0
test_cmpeq:
# REGISTER_IN r0 13
# REGISTER_IN r1 17
cmp/eq r0, r0
movt r2
cmp/eq r0, r2
movt r4
rts
nop
# REGISTER_OUT r2 1
# REGISTER_OUT r3 0
test_cmphs:
# REGISTER_IN r0 -1
# REGISTER_IN r1 13
cmp/hs r1, r0
movt r2
cmp/hs r1, r1
movt r3
cmp/hs r0, r1
movt r4
rts
nop
# REGISTER_OUT r2 1
# REGISTER_OUT r3 1
# REGISTER_OUT r4 0
test_cmpge:
# REGISTER_IN r0 -1
# REGISTER_IN r1 13
cmp/ge r1, r0
movt r2
cmp/ge r1, r1
movt r3
cmp/ge r0, r1
movt r4
rts
nop
# REGISTER_OUT r2 0
# REGISTER_OUT r3 1
# REGISTER_OUT r4 1
test_cmphi:
# REGISTER_IN r0 -1
# REGISTER_IN r1 13
cmp/hi r1, r0
movt r2
cmp/hi r1, r1
movt r3
cmp/hi r0, r1
movt r4
rts
nop
# REGISTER_OUT r2 1
# REGISTER_OUT r3 0
# REGISTER_OUT r4 0
test_cmpgt:
# REGISTER_IN r0 -1
# REGISTER_IN r1 13
cmp/gt r1, r0
movt r2
cmp/gt r1, r1
movt r3
cmp/gt r0, r1
movt r4
rts
nop
# REGISTER_OUT r2 0
# REGISTER_OUT r3 0
# REGISTER_OUT r4 1
test_cmppz:
# REGISTER_IN r0 -1
# REGISTER_IN r1 0
# REGISTER_IN r2 1
cmp/pz r0
movt r3
cmp/pz r1
movt r4
cmp/pz r2
movt r5
rts
nop
# REGISTER_OUT r3 0
# REGISTER_OUT r4 1
# REGISTER_OUT r5 1
test_cmppl:
# REGISTER_IN r0 -1
# REGISTER_IN r1 0
# REGISTER_IN r2 1
cmp/pl r0
movt r3
cmp/pl r1
movt r4
cmp/pl r2
movt r5
rts
nop
# REGISTER_OUT r3 0
# REGISTER_OUT r4 0
# REGISTER_OUT r5 1
test_cmpstr:
# REGISTER_IN r0 0x00000000
# REGISTER_IN r1 0xffffffff
@ -120,12 +5,6 @@ test_cmpstr:
# REGISTER_IN r3 0x00ff0000
cmp/str r0, r1
movt r4
cmp/str r2, r1
movt r5
cmp/str r3, r1
movt r6
rts
nop
# REGISTER_OUT r4 0
# REGISTER_OUT r5 0
# REGISTER_OUT r6 1

View File

@ -0,0 +1,74 @@
#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"
using namespace re::jit::ir;
using namespace re::jit::ir::passes;
TEST(DeadCodeEliminationPassTest, Sanity) {
static const char *input =
"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 %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"
"i32 %6 = load_context i32 0xe4\n"
"i32 %7 = load_guest i32 %6\n"
"store_context i32 0xb4, i32 %7\n"
"i64 %8 = load_context i32 0x18\n"
"i32 %9 = load_context i32 0x38\n"
"store_context i32 0x38, i32 %7\n"
"i64 %10 = zext i32 %9\n"
"i32 %11 = load_context i32 0x28\n"
"i32 %12 = sub i32 %11, i32 0xa\n"
"store_context i32 0x28, i32 %12\n"
"i32 %13 = load_context i32 0x2c\n"
"i32 %14 = add i32 %13, i32 0x7\n"
"store_context i32 0x2c, i32 %14\n"
"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"
"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"
"i32 %4 = load_context i32 0xe4\n"
"i32 %5 = load_guest i32 %4\n"
"store_context i32 0xb4, i32 %5\n"
"i64 %6 = load_context i32 0x18\n"
"i32 %7 = load_context i32 0x38\n"
"store_context i32 0x38, i32 %5\n"
"i64 %8 = zext i32 %7\n"
"i32 %9 = load_context i32 0x28\n"
"i32 %10 = sub i32 %9, i32 0xa\n"
"store_context i32 0x28, i32 %10\n"
"i32 %11 = load_context i32 0x2c\n"
"i32 %12 = add i32 %11, i32 0x7\n"
"store_context i32 0x2c, i32 %12\n"
"call_external i64 %6, i64 %8\n"
"store_context i32 0x30, i32 0x8c000940\n";
IRBuilder builder;
IRReader reader;
std::stringstream input_stream(input);
reader.Parse(input_stream, builder);
DeadCodeEliminationPass pass;
pass.Run(builder, false);
IRWriter writer;
std::stringstream output_stream;
writer.Print(builder, output_stream);
ASSERT_STREQ(output_stream.str().c_str(), output);
}

View File

@ -65,7 +65,7 @@ TEST(LoadStoreEliminationPassTest, Aliasing) {
reader.Parse(input_stream, builder);
LoadStoreEliminationPass pass;
pass.Run(builder);
pass.Run(builder, false);
IRWriter writer;
std::stringstream output_stream;

View File

@ -137,10 +137,6 @@ int sh4_num_test_regs =
fr0_out, fr1_out, fr2_out, fr3_out, fr4_out, fr5_out, fr6_out, fr7_out, fr8_out, fr9_out, fr10_out, fr11_out, fr12_out, fr13_out, fr14_out, fr15_out, \
xf0_out, xf1_out, xf2_out, xf3_out, xf4_out, xf5_out, xf6_out, xf7_out, xf8_out, xf9_out, xf10_out, xf11_out, xf12_out, xf13_out, xf14_out, xf15_out) \
}; \
TEST(sh4_interpreter, name) { \
FLAGS_interpreter = true; \
RunSH4Test(test_##name); \
} \
TEST(sh4_x64, name) { \
FLAGS_interpreter = false; \
RunSH4Test(test_##name); \

View File

@ -21,15 +21,7 @@ TEST_SH4(test_bsr,(uint8_t *)"\x22\x4f\x04\xb0\x01\x70\x03\x70\x26\x4f\x0b\x00\x
TEST_SH4(test_bsrf,(uint8_t *)"\x22\x4f\x03\x00\x01\x71\x03\x71\x26\x4f\x0b\x00\x09\x00\x09\x71\x0b\x00\x09\x00",20,0x0,0xbaadf00d,0x8,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_bt,(uint8_t *)"\x07\x88\x01\x89\x0b\x00\x09\x00\x03\xe1\x0b\x00\x09\x00\x07\x88\x02\x8d\x06\x71\x0b\x00\x09\x00\x07\x71\x0b\x00\x09\x00",30,0x0,0xbaadf00d,0x7,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,0x3,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_bts,(uint8_t *)"\x07\x88\x01\x89\x0b\x00\x09\x00\x03\xe1\x0b\x00\x09\x00\x07\x88\x02\x8d\x06\x71\x0b\x00\x09\x00\x07\x71\x0b\x00\x09\x00",30,0xe,0xbaadf00d,0x7,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,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_cmpgt,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x48,0xbaadf00d,0xffffffff,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,0x0,0x0,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_cmpstr,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x78,0xbaadf00d,0x0,0xffffffff,0xf00000,0xff0000,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x0,0x0,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_cmphs,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x18,0xbaadf00d,0xffffffff,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,0x1,0x1,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)
TEST_SH4(test_cmppz,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x58,0xbaadf00d,0xffffffff,0x0,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x0,0x1,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_cmppl,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x68,0xbaadf00d,0xffffffff,0x0,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0x0,0x0,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_cmphi,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x38,0xbaadf00d,0xffffffff,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,0x1,0x0,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)
TEST_SH4(test_cmpge,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0x28,0xbaadf00d,0xffffffff,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,0x0,0x1,0x1,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_cmpeq_imm,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,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,0x1,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)
TEST_SH4(test_cmpeq,(uint8_t *)"\x0d\x88\x29\x01\x11\x88\x29\x02\x0b\x00\x09\x00\x00\x30\x29\x02\x00\x32\x29\x04\x0b\x00\x09\x00\x12\x30\x29\x02\x12\x31\x29\x03\x02\x31\x29\x04\x0b\x00\x09\x00\x13\x30\x29\x02\x13\x31\x29\x03\x03\x31\x29\x04\x0b\x00\x09\x00\x16\x30\x29\x02\x16\x31\x29\x03\x06\x31\x29\x04\x0b\x00\x09\x00\x17\x30\x29\x02\x17\x31\x29\x03\x07\x31\x29\x04\x0b\x00\x09\x00\x11\x40\x29\x03\x11\x41\x29\x04\x11\x42\x29\x05\x0b\x00\x09\x00\x15\x40\x29\x03\x15\x41\x29\x04\x15\x42\x29\x05\x0b\x00\x09\x00\x0c\x21\x29\x04\x2c\x21\x29\x05\x3c\x21\x29\x06\x0b\x00\x09\x00",136,0xc,0xbaadf00d,0xd,0x11,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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,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)
TEST_SH4(test_cmpstr,(uint8_t *)"\x0c\x21\x29\x04\x0b\x00\x09\x00",8,0x0,0xbaadf00d,0x0,0xffffffff,0xf00000,0xff0000,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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)
TEST_SH4(test_div0s_pdividend_pdivisor,(uint8_t *)"\x0e\x40\x19\x00\x29\x01\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00",50,0x14,0xbaadf00d,0x700000f0,0xfffffffe,0xfffffffc,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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)
TEST_SH4(test_div0s_ndividend_pdivisor,(uint8_t *)"\x0e\x40\x19\x00\x29\x01\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00",50,0x1e,0xbaadf00d,0x700000f0,0x2,0xfffffffc,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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_div0s_ndividend_ndivisor,(uint8_t *)"\x0e\x40\x19\x00\x29\x01\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00\x0e\x40\x17\x22\x29\x03\x0b\x00\x09\x00",50,0xa,0xbaadf00d,0x700000f0,0x2,0x4,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,0xbaadf00d,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)