Merge remote-tracking branch 'tilkax/master'
This commit is contained in:
commit
b4aa01ccf4
14 changed files with 497 additions and 116 deletions
|
@ -3,6 +3,7 @@ project(dynarmic)
|
||||||
|
|
||||||
# Dynarmic project options
|
# Dynarmic project options
|
||||||
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
||||||
|
option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF)
|
||||||
|
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
|
@ -81,6 +82,14 @@ include_directories(${Boost_INCLUDE_DIRS})
|
||||||
include_directories(externals/catch)
|
include_directories(externals/catch)
|
||||||
enable_testing(true) # Enables unit-testing.
|
enable_testing(true) # Enables unit-testing.
|
||||||
|
|
||||||
|
# Include LLVM
|
||||||
|
if (DYNARMIC_USE_LLVM)
|
||||||
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
|
include_directories(${LLVM_INCLUDE_DIRS})
|
||||||
|
add_definitions(-DDYNARMIC_USE_LLVM ${LLVM_DEFINITIONS})
|
||||||
|
llvm_map_components_to_libnames(llvm_libs x86desc x86disassembler)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Dynarmic project files
|
# Dynarmic project files
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|
|
@ -48,13 +48,13 @@ static void EraseInstruction(IR::Block& block, IR::Inst* inst) {
|
||||||
block.instructions.erase(block.instructions.iterator_to(*inst));
|
block.instructions.erase(block.instructions.iterator_to(*inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) {
|
EmitX64::BlockDescriptor* EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) {
|
||||||
inhibit_emission.clear();
|
inhibit_emission.clear();
|
||||||
reg_alloc.Reset();
|
reg_alloc.Reset();
|
||||||
|
|
||||||
code->INT3();
|
code->INT3();
|
||||||
CodePtr code_ptr = code->GetCodePtr();
|
CodePtr code_ptr = code->GetCodePtr();
|
||||||
basic_blocks[descriptor] = code_ptr;
|
basic_blocks[descriptor].code_ptr = code_ptr;
|
||||||
|
|
||||||
EmitCondPrelude(block.cond, block.cond_failed, block.location);
|
EmitCondPrelude(block.cond, block.cond_failed, block.location);
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Bl
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
||||||
return code_ptr;
|
basic_blocks[descriptor].size = code->GetCodePtr() - code_ptr;
|
||||||
|
return &basic_blocks[descriptor];
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitBreakpoint(IR::Block&, IR::Inst*) {
|
void EmitX64::EmitBreakpoint(IR::Block&, IR::Inst*) {
|
||||||
|
@ -276,6 +277,24 @@ void EmitX64::EmitGetOverflowFromOp(IR::Block&, IR::Inst*) {
|
||||||
ASSERT_MSG(0, "should never happen");
|
ASSERT_MSG(0, "should never happen");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitPack2x32To1x64(IR::Block&, IR::Inst* inst) {
|
||||||
|
auto lo = reg_alloc.UseRegister(inst->GetArg(0), any_gpr);
|
||||||
|
auto hi = reg_alloc.UseDefRegister(inst->GetArg(1), inst, any_gpr);
|
||||||
|
code->SHL(64, R(hi), Imm8(32));
|
||||||
|
code->OR(64, R(hi), R(lo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitLeastSignificantWord(IR::Block&, IR::Inst* inst) {
|
||||||
|
// TODO: Optimize
|
||||||
|
auto u64 = reg_alloc.UseDefRegister(inst->GetArg(0), inst, any_gpr);
|
||||||
|
code->MOVZX(64, 32, u64, R(u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitMostSignificantWord(IR::Block&, IR::Inst* inst) {
|
||||||
|
auto u64 = reg_alloc.UseDefRegister(inst->GetArg(0), inst, any_gpr);
|
||||||
|
code->SHR(64, R(u64), Imm8(32));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitLeastSignificantHalf(IR::Block&, IR::Inst* inst) {
|
void EmitX64::EmitLeastSignificantHalf(IR::Block&, IR::Inst* inst) {
|
||||||
reg_alloc.RegisterAddDef(inst, inst->GetArg(0));
|
reg_alloc.RegisterAddDef(inst, inst->GetArg(0));
|
||||||
}
|
}
|
||||||
|
@ -302,6 +321,16 @@ void EmitX64::EmitIsZero(IR::Block&, IR::Inst* inst) {
|
||||||
code->MOVZX(32, 8, result, R(result));
|
code->MOVZX(32, 8, result, R(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitIsZero64(IR::Block&, IR::Inst* inst) {
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst, any_gpr);
|
||||||
|
|
||||||
|
// TODO: Flag optimization
|
||||||
|
|
||||||
|
code->TEST(64, R(result), R(result));
|
||||||
|
code->SETcc(CCFlags::CC_E, R(result));
|
||||||
|
code->MOVZX(32, 8, result, R(result));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitLogicalShiftLeft(IR::Block& block, IR::Inst* inst) {
|
void EmitX64::EmitLogicalShiftLeft(IR::Block& block, IR::Inst* inst) {
|
||||||
auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp);
|
auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp);
|
||||||
|
|
||||||
|
@ -704,6 +733,16 @@ void EmitX64::EmitAddWithCarry(IR::Block& block, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitAdd64(IR::Block& block, IR::Inst* inst) {
|
||||||
|
IR::Value a = inst->GetArg(0);
|
||||||
|
IR::Value b = inst->GetArg(1);
|
||||||
|
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(a, inst, any_gpr);
|
||||||
|
OpArg op_arg = R(reg_alloc.UseRegister(b, any_gpr));
|
||||||
|
|
||||||
|
code->ADD(64, R(result), op_arg);
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitSubWithCarry(IR::Block& block, IR::Inst* inst) {
|
void EmitX64::EmitSubWithCarry(IR::Block& block, IR::Inst* inst) {
|
||||||
auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp);
|
auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp);
|
||||||
auto overflow_inst = FindUseWithOpcode(inst, IR::Opcode::GetOverflowFromOp);
|
auto overflow_inst = FindUseWithOpcode(inst, IR::Opcode::GetOverflowFromOp);
|
||||||
|
@ -747,6 +786,28 @@ void EmitX64::EmitSubWithCarry(IR::Block& block, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitMul(IR::Block&, IR::Inst* inst) {
|
||||||
|
IR::Value a = inst->GetArg(0);
|
||||||
|
IR::Value b = inst->GetArg(1);
|
||||||
|
if (a.IsImmediate())
|
||||||
|
std::swap(a, b);
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(a, inst, any_gpr);
|
||||||
|
if (b.IsImmediate()) {
|
||||||
|
code->IMUL(32, result, R(result), Imm32(b.GetU32()));
|
||||||
|
} else {
|
||||||
|
OpArg op_arg = R(reg_alloc.UseRegister(b.GetInst(), any_gpr));
|
||||||
|
code->IMUL(32, result, op_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitMul64(IR::Block&, IR::Inst* inst) {
|
||||||
|
IR::Value a = inst->GetArg(0);
|
||||||
|
IR::Value b = inst->GetArg(1);
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(a, inst, any_gpr);
|
||||||
|
OpArg op_arg = R(reg_alloc.UseRegister(b.GetInst(), any_gpr));
|
||||||
|
code->IMUL(64, result, op_arg);
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitAnd(IR::Block&, IR::Inst* inst) {
|
void EmitX64::EmitAnd(IR::Block&, IR::Inst* inst) {
|
||||||
IR::Value a = inst->GetArg(0);
|
IR::Value a = inst->GetArg(0);
|
||||||
IR::Value b = inst->GetArg(1);
|
IR::Value b = inst->GetArg(1);
|
||||||
|
@ -791,6 +852,13 @@ void EmitX64::EmitNot(IR::Block&, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitSignExtendWordToLong(IR::Block&, IR::Inst* inst) {
|
||||||
|
// TODO: Remove unnecessary mov that may occur here
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst, any_gpr);
|
||||||
|
|
||||||
|
code->MOVSX(64, 32, result, R(result));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitSignExtendHalfToWord(IR::Block&, IR::Inst* inst) {
|
void EmitX64::EmitSignExtendHalfToWord(IR::Block&, IR::Inst* inst) {
|
||||||
OpArg source;
|
OpArg source;
|
||||||
X64Reg result;
|
X64Reg result;
|
||||||
|
@ -807,6 +875,13 @@ void EmitX64::EmitSignExtendByteToWord(IR::Block&, IR::Inst* inst) {
|
||||||
code->MOVSX(32, 8, result, source);
|
code->MOVSX(32, 8, result, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitZeroExtendWordToLong(IR::Block&, IR::Inst* inst) {
|
||||||
|
// TODO: Remove unnecessary mov that may occur here
|
||||||
|
X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst, any_gpr);
|
||||||
|
|
||||||
|
code->MOVZX(64, 32, result, R(result));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitZeroExtendHalfToWord(IR::Block&, IR::Inst* inst) {
|
void EmitX64::EmitZeroExtendHalfToWord(IR::Block&, IR::Inst* inst) {
|
||||||
OpArg source;
|
OpArg source;
|
||||||
X64Reg result;
|
X64Reg result;
|
||||||
|
|
|
@ -23,11 +23,15 @@ public:
|
||||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
||||||
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
||||||
|
|
||||||
CodePtr Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir);
|
struct BlockDescriptor {
|
||||||
|
CodePtr code_ptr;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
BlockDescriptor* Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir);
|
||||||
|
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
auto iter = basic_blocks.find(descriptor);
|
auto iter = basic_blocks.find(descriptor);
|
||||||
return iter != basic_blocks.end() ? iter->second : nullptr;
|
return iter != basic_blocks.end() ? &iter->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache();
|
void ClearCache();
|
||||||
|
@ -62,7 +66,7 @@ private:
|
||||||
Routines* routines;
|
Routines* routines;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
Jit* jit_interface;
|
Jit* jit_interface;
|
||||||
std::unordered_map<Arm::LocationDescriptor, CodePtr, Arm::LocationDescriptorHash> basic_blocks;
|
std::unordered_map<Arm::LocationDescriptor, BlockDescriptor, Arm::LocationDescriptorHash> basic_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef DYNARMIC_USE_LLVM
|
||||||
|
#include <llvm-c/Disassembler.h>
|
||||||
|
#include <llvm-c/Target.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "backend_x64/emit_x64.h"
|
#include "backend_x64/emit_x64.h"
|
||||||
#include "backend_x64/jitstate.h"
|
#include "backend_x64/jitstate.h"
|
||||||
#include "backend_x64/routines.h"
|
#include "backend_x64/routines.h"
|
||||||
|
@ -13,6 +18,7 @@
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/translate/translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
@ -44,14 +50,52 @@ struct Jit::Impl {
|
||||||
|
|
||||||
Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.Fpscr};
|
Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.Fpscr};
|
||||||
|
|
||||||
CodePtr code_ptr = GetBasicBlock(descriptor);
|
CodePtr code_ptr = GetBasicBlock(descriptor)->code_ptr;
|
||||||
return routines.RunCode(&jit_state, code_ptr, cycle_count);
|
return routines.RunCode(&jit_state, code_ptr, cycle_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Disassemble(Arm::LocationDescriptor descriptor) {
|
||||||
|
auto block = GetBasicBlock(descriptor);
|
||||||
|
std::string result = Common::StringFromFormat("address: %p\nsize: %zu bytes\n", block->code_ptr, block->size);
|
||||||
|
|
||||||
|
#ifdef DYNARMIC_USE_LLVM
|
||||||
|
CodePtr end = block->code_ptr + block->size;
|
||||||
|
size_t remaining = block->size;
|
||||||
|
|
||||||
|
LLVMInitializeX86TargetInfo();
|
||||||
|
LLVMInitializeX86TargetMC();
|
||||||
|
LLVMInitializeX86Disassembler();
|
||||||
|
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("x86_64", nullptr, 0, nullptr, nullptr);
|
||||||
|
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||||
|
|
||||||
|
for (CodePtr pos = block->code_ptr; pos < end;) {
|
||||||
|
char buffer[80];
|
||||||
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(pos), remaining, (u64)pos, buffer, sizeof(buffer));
|
||||||
|
assert(inst_size);
|
||||||
|
for (CodePtr i = pos; i < pos + inst_size; i++)
|
||||||
|
result.append(Common::StringFromFormat("%02x ", *i));
|
||||||
|
for (size_t i = inst_size; i < 10; i++)
|
||||||
|
result.append(" ");
|
||||||
|
result.append(buffer);
|
||||||
|
result.append("\n");
|
||||||
|
|
||||||
|
pos += inst_size;
|
||||||
|
remaining -= inst_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMDisasmDispose(llvm_ctx);
|
||||||
|
#else
|
||||||
|
result.append("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
EmitX64::BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
CodePtr code_ptr = emitter.GetBasicBlock(descriptor);
|
auto block = emitter.GetBasicBlock(descriptor);
|
||||||
if (code_ptr)
|
if (block)
|
||||||
return code_ptr;
|
return block;
|
||||||
|
|
||||||
IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32);
|
IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32);
|
||||||
Optimization::GetSetElimination(ir_block);
|
Optimization::GetSetElimination(ir_block);
|
||||||
|
@ -113,4 +157,8 @@ u32 Jit::Cpsr() const {
|
||||||
return impl->jit_state.Cpsr;
|
return impl->jit_state.Cpsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Jit::Disassemble(Arm::LocationDescriptor descriptor) {
|
||||||
|
return impl->Disassemble(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -246,15 +246,15 @@ boost::optional<const ArmMatcher<V>&> DecodeArm(u32 instruction) {
|
||||||
//INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn"), // v6
|
//INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn"), // v6
|
||||||
|
|
||||||
// Multiply (Normal) instructions
|
// Multiply (Normal) instructions
|
||||||
//INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn"), // v2
|
INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn"), // v2
|
||||||
//INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn"), // v2
|
INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn"), // v2
|
||||||
|
|
||||||
// Multiply (Long) instructions
|
// Multiply (Long) instructions
|
||||||
//INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn"), // v3M
|
INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn"), // v3M
|
||||||
//INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn"), // v3M
|
INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn"), // v3M
|
||||||
//INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn"), // v6
|
INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn"), // v6
|
||||||
//INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn"), // v3M
|
INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn"), // v3M
|
||||||
//INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn"), // v3M
|
INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn"), // v3M
|
||||||
|
|
||||||
// Multiply (Halfword) instructions
|
// Multiply (Halfword) instructions
|
||||||
//INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn"), // v5xP
|
//INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn"), // v5xP
|
||||||
|
|
|
@ -383,15 +383,29 @@ public:
|
||||||
std::string arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
|
std::string arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
|
||||||
|
|
||||||
// Multiply (Normal) instructions
|
// Multiply (Normal) instructions
|
||||||
std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { return "ice"; }
|
std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) {
|
||||||
std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { return "ice"; }
|
return Common::StringFromFormat("mla%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(d), RegToString(n), RegToString(m), RegToString(a));
|
||||||
|
}
|
||||||
|
std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) {
|
||||||
|
return Common::StringFromFormat("mul%s%s %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(d), RegToString(n), RegToString(m));
|
||||||
|
}
|
||||||
|
|
||||||
// Multiply (Long) instructions
|
// Multiply (Long) instructions
|
||||||
std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
return Common::StringFromFormat("smlal%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m));
|
||||||
std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
}
|
||||||
std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
return Common::StringFromFormat("smull%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m));
|
||||||
|
}
|
||||||
|
std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
|
return Common::StringFromFormat("umaal%s %s, %s, %s, %s", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m));
|
||||||
|
}
|
||||||
|
std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
|
return Common::StringFromFormat("umlal%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m));
|
||||||
|
}
|
||||||
|
std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
|
return Common::StringFromFormat("umull%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m));
|
||||||
|
}
|
||||||
|
|
||||||
// Multiply (Halfword) instructions
|
// Multiply (Halfword) instructions
|
||||||
std::string arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { return "ice"; }
|
std::string arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { return "ice"; }
|
||||||
|
|
|
@ -98,6 +98,19 @@ void IREmitter::SetVFlag(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::SetVFlag, {value});
|
Inst(IR::Opcode::SetVFlag, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::Pack2x32To1x64(const IR::Value& lo, const IR::Value& hi)
|
||||||
|
{
|
||||||
|
return Inst(IR::Opcode::Pack2x32To1x64, {lo, hi});
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::LeastSignificantWord(const IR::Value& value) {
|
||||||
|
return Inst(IR::Opcode::LeastSignificantWord, {value});
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::MostSignificantWord(const IR::Value& value) {
|
||||||
|
return Inst(IR::Opcode::MostSignificantWord, {value});
|
||||||
|
}
|
||||||
|
|
||||||
IR::Value IREmitter::LeastSignificantHalf(const IR::Value& value) {
|
IR::Value IREmitter::LeastSignificantHalf(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::LeastSignificantHalf, {value});
|
return Inst(IR::Opcode::LeastSignificantHalf, {value});
|
||||||
}
|
}
|
||||||
|
@ -114,6 +127,10 @@ IR::Value IREmitter::IsZero(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::IsZero, {value});
|
return Inst(IR::Opcode::IsZero, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::IsZero64(const IR::Value& value) {
|
||||||
|
return Inst(IR::Opcode::IsZero64, {value});
|
||||||
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
|
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
|
@ -155,6 +172,10 @@ IR::Value IREmitter::Add(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::AddWithCarry, {a, b, Imm1(0)});
|
return Inst(IR::Opcode::AddWithCarry, {a, b, Imm1(0)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::Add64(const IR::Value& a, const IR::Value& b) {
|
||||||
|
return Inst(IR::Opcode::Add64, {a, b});
|
||||||
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in) {
|
IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in) {
|
||||||
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
|
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
|
||||||
auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in});
|
auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in});
|
||||||
|
@ -167,6 +188,28 @@ IR::Value IREmitter::Sub(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::Mul(const IR::Value& a, const IR::Value& b) {
|
||||||
|
return Inst(IR::Opcode::Mul, {a, b});
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::Mul64(const IR::Value& a, const IR::Value& b) {
|
||||||
|
return Inst(IR::Opcode::Mul64, {a, b});
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::SignedMulHi(const IR::Value& a, const IR::Value& b) {
|
||||||
|
auto a64 = ZeroExtendWordToLong(a);
|
||||||
|
auto b64 = ZeroExtendWordToLong(b);
|
||||||
|
auto product64 = Mul64(a64, b64);
|
||||||
|
return LogicalShiftRight(product64, Imm8(32), Imm8(0)).result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::UnsignedMulHi(const IR::Value& a, const IR::Value& b) {
|
||||||
|
auto a64 = SignExtendWordToLong(a);
|
||||||
|
auto b64 = SignExtendWordToLong(b);
|
||||||
|
auto product64 = Mul64(a64, b64);
|
||||||
|
return LogicalShiftRight(product64, Imm8(32), Imm8(0)).result;
|
||||||
|
}
|
||||||
|
|
||||||
IR::Value IREmitter::And(const IR::Value& a, const IR::Value& b) {
|
IR::Value IREmitter::And(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::And, {a, b});
|
return Inst(IR::Opcode::And, {a, b});
|
||||||
}
|
}
|
||||||
|
@ -183,6 +226,10 @@ IR::Value IREmitter::Not(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::Not, {a});
|
return Inst(IR::Opcode::Not, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::SignExtendWordToLong(const IR::Value& a) {
|
||||||
|
return Inst(IR::Opcode::SignExtendWordToLong, {a});
|
||||||
|
}
|
||||||
|
|
||||||
IR::Value IREmitter::SignExtendHalfToWord(const IR::Value& a) {
|
IR::Value IREmitter::SignExtendHalfToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::SignExtendHalfToWord, {a});
|
return Inst(IR::Opcode::SignExtendHalfToWord, {a});
|
||||||
}
|
}
|
||||||
|
@ -191,6 +238,10 @@ IR::Value IREmitter::SignExtendByteToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::SignExtendByteToWord, {a});
|
return Inst(IR::Opcode::SignExtendByteToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Value IREmitter::ZeroExtendWordToLong(const IR::Value& a) {
|
||||||
|
return Inst(IR::Opcode::ZeroExtendWordToLong, {a});
|
||||||
|
}
|
||||||
|
|
||||||
IR::Value IREmitter::ZeroExtendHalfToWord(const IR::Value& a) {
|
IR::Value IREmitter::ZeroExtendHalfToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ZeroExtendHalfToWord, {a});
|
return Inst(IR::Opcode::ZeroExtendHalfToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,14 @@ public:
|
||||||
void SetCFlag(const IR::Value& value);
|
void SetCFlag(const IR::Value& value);
|
||||||
void SetVFlag(const IR::Value& value);
|
void SetVFlag(const IR::Value& value);
|
||||||
|
|
||||||
|
IR::Value Pack2x32To1x64(const IR::Value& lo, const IR::Value& hi);
|
||||||
|
IR::Value LeastSignificantWord(const IR::Value& value);
|
||||||
|
IR::Value MostSignificantWord(const IR::Value& value);
|
||||||
IR::Value LeastSignificantHalf(const IR::Value& value);
|
IR::Value LeastSignificantHalf(const IR::Value& value);
|
||||||
IR::Value LeastSignificantByte(const IR::Value& value);
|
IR::Value LeastSignificantByte(const IR::Value& value);
|
||||||
IR::Value MostSignificantBit(const IR::Value& value);
|
IR::Value MostSignificantBit(const IR::Value& value);
|
||||||
IR::Value IsZero(const IR::Value& value);
|
IR::Value IsZero(const IR::Value& value);
|
||||||
|
IR::Value IsZero64(const IR::Value& value);
|
||||||
|
|
||||||
ResultAndCarry LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
ResultAndCarry LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
ResultAndCarry LogicalShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
ResultAndCarry LogicalShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
|
@ -66,14 +70,21 @@ public:
|
||||||
ResultAndCarry RotateRightExtended(const IR::Value& value_in, const IR::Value& carry_in);
|
ResultAndCarry RotateRightExtended(const IR::Value& value_in, const IR::Value& carry_in);
|
||||||
ResultAndCarryAndOverflow AddWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
ResultAndCarryAndOverflow AddWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
||||||
IR::Value Add(const IR::Value& a, const IR::Value& b);
|
IR::Value Add(const IR::Value& a, const IR::Value& b);
|
||||||
|
IR::Value Add64(const IR::Value& a, const IR::Value& b);
|
||||||
ResultAndCarryAndOverflow SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
ResultAndCarryAndOverflow SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
||||||
IR::Value Sub(const IR::Value& a, const IR::Value& b);
|
IR::Value Sub(const IR::Value& a, const IR::Value& b);
|
||||||
|
IR::Value Mul(const IR::Value& a, const IR::Value& b);
|
||||||
|
IR::Value Mul64(const IR::Value& a, const IR::Value& b);
|
||||||
|
IR::Value SignedMulHi(const IR::Value& a, const IR::Value& b);
|
||||||
|
IR::Value UnsignedMulHi(const IR::Value& a, const IR::Value& b);
|
||||||
IR::Value And(const IR::Value& a, const IR::Value& b);
|
IR::Value And(const IR::Value& a, const IR::Value& b);
|
||||||
IR::Value Eor(const IR::Value& a, const IR::Value& b);
|
IR::Value Eor(const IR::Value& a, const IR::Value& b);
|
||||||
IR::Value Or(const IR::Value& a, const IR::Value& b);
|
IR::Value Or(const IR::Value& a, const IR::Value& b);
|
||||||
IR::Value Not(const IR::Value& a);
|
IR::Value Not(const IR::Value& a);
|
||||||
|
IR::Value SignExtendWordToLong(const IR::Value& a);
|
||||||
IR::Value SignExtendHalfToWord(const IR::Value& a);
|
IR::Value SignExtendHalfToWord(const IR::Value& a);
|
||||||
IR::Value SignExtendByteToWord(const IR::Value& a);
|
IR::Value SignExtendByteToWord(const IR::Value& a);
|
||||||
|
IR::Value ZeroExtendWordToLong(const IR::Value& a);
|
||||||
IR::Value ZeroExtendHalfToWord(const IR::Value& a);
|
IR::Value ZeroExtendHalfToWord(const IR::Value& a);
|
||||||
IR::Value ZeroExtendByteToWord(const IR::Value& a);
|
IR::Value ZeroExtendByteToWord(const IR::Value& a);
|
||||||
IR::Value ByteReverseWord(const IR::Value& a);
|
IR::Value ByteReverseWord(const IR::Value& a);
|
||||||
|
|
|
@ -22,10 +22,14 @@ OPCODE(GetCarryFromOp, T::U1, T::U32
|
||||||
OPCODE(GetOverflowFromOp, T::U1, T::U32 )
|
OPCODE(GetOverflowFromOp, T::U1, T::U32 )
|
||||||
|
|
||||||
// Calculations
|
// Calculations
|
||||||
|
OPCODE(Pack2x32To1x64, T::U64, T::U32, T::U32 )
|
||||||
|
OPCODE(LeastSignificantWord, T::U32, T::U64 )
|
||||||
|
OPCODE(MostSignificantWord, T::U32, T::U64 )
|
||||||
OPCODE(LeastSignificantHalf, T::U16, T::U32 )
|
OPCODE(LeastSignificantHalf, T::U16, T::U32 )
|
||||||
OPCODE(LeastSignificantByte, T::U8, T::U32 )
|
OPCODE(LeastSignificantByte, T::U8, T::U32 )
|
||||||
OPCODE(MostSignificantBit, T::U1, T::U32 )
|
OPCODE(MostSignificantBit, T::U1, T::U32 )
|
||||||
OPCODE(IsZero, T::U1, T::U32 )
|
OPCODE(IsZero, T::U1, T::U32 )
|
||||||
|
OPCODE(IsZero64, T::U1, T::U64 )
|
||||||
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
|
||||||
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )
|
||||||
OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 )
|
||||||
|
@ -33,12 +37,17 @@ OPCODE(RotateRight, T::U32, T::U32, T::U8,
|
||||||
OPCODE(RotateRightExtended, T::U32, T::U32, T::U1 )
|
OPCODE(RotateRightExtended, T::U32, T::U32, T::U1 )
|
||||||
OPCODE(AddWithCarry, T::U32, T::U32, T::U32, T::U1 )
|
OPCODE(AddWithCarry, T::U32, T::U32, T::U32, T::U1 )
|
||||||
OPCODE(SubWithCarry, T::U32, T::U32, T::U32, T::U1 )
|
OPCODE(SubWithCarry, T::U32, T::U32, T::U32, T::U1 )
|
||||||
|
OPCODE(Add64, T::U64, T::U64, T::U64 )
|
||||||
|
OPCODE(Mul, T::U32, T::U32, T::U32 )
|
||||||
|
OPCODE(Mul64, T::U64, T::U64, T::U64 )
|
||||||
OPCODE(And, T::U32, T::U32, T::U32 )
|
OPCODE(And, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Eor, T::U32, T::U32, T::U32 )
|
OPCODE(Eor, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Or, T::U32, T::U32, T::U32 )
|
OPCODE(Or, T::U32, T::U32, T::U32 )
|
||||||
OPCODE(Not, T::U32, T::U32 )
|
OPCODE(Not, T::U32, T::U32 )
|
||||||
|
OPCODE(SignExtendWordToLong, T::U64, T::U32 )
|
||||||
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
|
OPCODE(SignExtendHalfToWord, T::U32, T::U16 )
|
||||||
OPCODE(SignExtendByteToWord, T::U32, T::U8 )
|
OPCODE(SignExtendByteToWord, T::U32, T::U8 )
|
||||||
|
OPCODE(ZeroExtendWordToLong, T::U64, T::U32 )
|
||||||
OPCODE(ZeroExtendHalfToWord, T::U32, T::U16 )
|
OPCODE(ZeroExtendHalfToWord, T::U32, T::U16 )
|
||||||
OPCODE(ZeroExtendByteToWord, T::U32, T::U8 )
|
OPCODE(ZeroExtendByteToWord, T::U32, T::U8 )
|
||||||
OPCODE(ByteReverseWord, T::U32, T::U32 )
|
OPCODE(ByteReverseWord, T::U32, T::U32 )
|
||||||
|
|
|
@ -614,15 +614,70 @@ bool ArmTranslatorVisitor::arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, S
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
bool ArmTranslatorVisitor::arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||||
return InterpretThisInstruction();
|
if (ConditionPassed(cond)) {
|
||||||
|
u32 imm32 = ArmExpandImm(rotate, imm8);
|
||||||
|
auto result = ir.SubWithCarry(ir.Imm32(imm32), ir.GetRegister(n), ir.Imm1(1));
|
||||||
|
if (d == Reg::PC) {
|
||||||
|
ASSERT(!S);
|
||||||
|
ir.ALUWritePC(result.result);
|
||||||
|
ir.SetTerm(IR::Term::ReturnToDispatch{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ir.SetRegister(d, result.result);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
bool ArmTranslatorVisitor::arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||||
return InterpretThisInstruction();
|
if (ConditionPassed(cond)) {
|
||||||
|
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
|
||||||
|
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.Imm1(1));
|
||||||
|
if (d == Reg::PC) {
|
||||||
|
ASSERT(!S);
|
||||||
|
ir.ALUWritePC(result.result);
|
||||||
|
ir.SetTerm(IR::Term::ReturnToDispatch{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ir.SetRegister(d, result.result);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
bool ArmTranslatorVisitor::arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||||
return InterpretThisInstruction();
|
if (n == Reg::PC || m == Reg::PC || s == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
|
||||||
|
auto carry_in = ir.GetCFlag();
|
||||||
|
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
|
||||||
|
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.Imm1(1));
|
||||||
|
if (d == Reg::PC) {
|
||||||
|
ASSERT(!S);
|
||||||
|
ir.ALUWritePC(result.result);
|
||||||
|
ir.SetTerm(IR::Term::ReturnToDispatch{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ir.SetRegister(d, result.result);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
bool ArmTranslatorVisitor::arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||||
|
|
|
@ -11,33 +11,137 @@ namespace Arm {
|
||||||
|
|
||||||
// Multiply (Normal) instructions
|
// Multiply (Normal) instructions
|
||||||
bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a));
|
||||||
|
ir.SetRegister(d, result);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m));
|
||||||
|
ir.SetRegister(d, result);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Multiply (Long) instructions
|
// Multiply (Long) instructions
|
||||||
bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (dLo == dHi)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
|
||||||
|
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
|
||||||
|
auto product = ir.Mul64(n64, m64);
|
||||||
|
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
|
||||||
|
auto result = ir.Add64(product, addend);
|
||||||
|
auto lo = ir.LeastSignificantWord(result);
|
||||||
|
auto hi = ir.MostSignificantWord(result);
|
||||||
|
ir.SetRegister(dLo, lo);
|
||||||
|
ir.SetRegister(dHi, hi);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
|
ir.SetZFlag(ir.IsZero64(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (dLo == dHi)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
|
||||||
|
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
|
||||||
|
auto result = ir.Mul64(n64, m64);
|
||||||
|
auto lo = ir.LeastSignificantWord(result);
|
||||||
|
auto hi = ir.MostSignificantWord(result);
|
||||||
|
ir.SetRegister(dLo, lo);
|
||||||
|
ir.SetRegister(dHi, hi);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
|
ir.SetZFlag(ir.IsZero64(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (dLo == dHi)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo));
|
||||||
|
auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi));
|
||||||
|
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
|
||||||
|
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
|
||||||
|
auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64);
|
||||||
|
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
|
||||||
|
ir.SetRegister(dHi, ir.MostSignificantWord(result));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (dLo == dHi)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
|
||||||
|
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
|
||||||
|
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
|
||||||
|
auto result = ir.Add64(ir.Mul64(n64, m64), addend);
|
||||||
|
auto lo = ir.LeastSignificantWord(result);
|
||||||
|
auto hi = ir.MostSignificantWord(result);
|
||||||
|
ir.SetRegister(dLo, lo);
|
||||||
|
ir.SetRegister(dHi, hi);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
|
ir.SetZFlag(ir.IsZero64(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
|
||||||
return InterpretThisInstruction();
|
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (dLo == dHi)
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
|
||||||
|
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
|
||||||
|
auto result = ir.Mul64(n64, m64);
|
||||||
|
auto lo = ir.LeastSignificantWord(result);
|
||||||
|
auto hi = ir.MostSignificantWord(result);
|
||||||
|
ir.SetRegister(dLo, lo);
|
||||||
|
ir.SetRegister(dHi, hi);
|
||||||
|
if (S) {
|
||||||
|
ir.SetNFlag(ir.MostSignificantBit(hi));
|
||||||
|
ir.SetZFlag(ir.IsZero64(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -75,6 +76,8 @@ public:
|
||||||
return is_executing;
|
return is_executing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Disassemble(Arm::LocationDescriptor descriptor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool halt_requested = false;
|
bool halt_requested = false;
|
||||||
bool is_executing = false;
|
bool is_executing = false;
|
||||||
|
|
|
@ -33,5 +33,5 @@ set(HEADERS
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
add_executable(dynarmic_tests ${SRCS})
|
add_executable(dynarmic_tests ${SRCS})
|
||||||
target_link_libraries(dynarmic_tests dynarmic)
|
target_link_libraries(dynarmic_tests dynarmic ${llvm_libs})
|
||||||
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
|
|
|
@ -7,17 +7,18 @@
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
#include <frontend/ir/ir.h>
|
|
||||||
#include <ir_opt/passes.h>
|
|
||||||
#include <frontend/translate/translate.h>
|
|
||||||
|
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/disassembler/disassembler.h"
|
#include "frontend/disassembler/disassembler.h"
|
||||||
|
#include "frontend/ir/ir.h"
|
||||||
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
#include "ir_opt/passes.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
@ -140,12 +141,23 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u32 Generate() const {
|
u32 Generate(bool condition = true) const {
|
||||||
u32 inst;
|
u32 inst;
|
||||||
do {
|
do {
|
||||||
u32 random = RandInt<u32>(0, 0xFFFF);
|
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
|
||||||
|
if (condition)
|
||||||
|
random &= ~(0xF << 28);
|
||||||
inst = bits | (random & ~mask);
|
inst = bits | (random & ~mask);
|
||||||
} while (!is_valid(inst));
|
} while (!is_valid(inst));
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
// Have a one-in-twenty-five chance of actually having a cond.
|
||||||
|
if (RandInt(1, 25) == 1)
|
||||||
|
inst |= RandInt(0x0, 0xD) << 28;
|
||||||
|
else
|
||||||
|
inst |= 0xE << 28;
|
||||||
|
}
|
||||||
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
u32 Bits() { return bits; }
|
u32 Bits() { return bits; }
|
||||||
|
@ -219,22 +231,27 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
|
|
||||||
printf("\nInitial Register Listing: \n");
|
printf("\nInitial Register Listing: \n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (int i = 0; i <= 15; i++) {
|
||||||
printf("%4i: %08x\n", i, initial_regs[i]);
|
auto reg = Dynarmic::Arm::RegToString(static_cast<Dynarmic::Arm::Reg>(i));
|
||||||
|
printf("%4s: %08x\n", reg, initial_regs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nFinal Register Listing: \n");
|
printf("\nFinal Register Listing: \n");
|
||||||
printf(" interp jit\n");
|
printf(" interp jit\n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (int i = 0; i <= 15; i++) {
|
||||||
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
auto reg = Dynarmic::Arm::RegToString(static_cast<Dynarmic::Arm::Reg>(i));
|
||||||
|
printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
||||||
|
|
||||||
Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate({0, false, false, 0}, &MemoryRead32);
|
Dynarmic::Arm::LocationDescriptor descriptor = {0, false, false, 0};
|
||||||
|
Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate(descriptor, &MemoryRead32);
|
||||||
Dynarmic::Optimization::GetSetElimination(ir_block);
|
Dynarmic::Optimization::GetSetElimination(ir_block);
|
||||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||||
Dynarmic::Optimization::VerificationPass(ir_block);
|
Dynarmic::Optimization::VerificationPass(ir_block);
|
||||||
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
|
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
|
||||||
|
|
||||||
|
printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str());
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
#else
|
#else
|
||||||
|
@ -444,46 +461,20 @@ TEST_CASE("Fuzz ARM reversal instructions", "[JitX64]") {
|
||||||
|
|
||||||
const std::array<InstructionGenerator, 3> rev_instructions = {
|
const std::array<InstructionGenerator, 3> rev_instructions = {
|
||||||
{
|
{
|
||||||
InstructionGenerator("0000011010111111dddd11110011mmmm", is_valid),
|
InstructionGenerator("cccc011010111111dddd11110011mmmm", is_valid),
|
||||||
InstructionGenerator("0000011010111111dddd11111011mmmm", is_valid),
|
InstructionGenerator("cccc011010111111dddd11111011mmmm", is_valid),
|
||||||
InstructionGenerator("0000011011111111dddd11111011mmmm", is_valid),
|
InstructionGenerator("cccc011011111111dddd11111011mmmm", is_valid),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("REV tests") {
|
SECTION("Reverse tests") {
|
||||||
FuzzJitArm(1, 1, 10000, [&rev_instructions]() -> u32 {
|
FuzzJitArm(1, 1, 10000, [&rev_instructions]() -> u32 {
|
||||||
u32 cond = 0xE;
|
return rev_instructions[RandInt<size_t>(0, rev_instructions.size() - 1)].Generate();
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return rev_instructions[0].Generate() | (cond << 28);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("REV16 tests") {
|
|
||||||
FuzzJitArm(1, 1, 10000, [&rev_instructions]() -> u32 {
|
|
||||||
u32 cond = 0xE;
|
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return rev_instructions[1].Generate() | (cond << 28);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("REVSH tests") {
|
|
||||||
FuzzJitArm(1, 1, 10000, [&rev_instructions]() -> u32 {
|
|
||||||
u32 cond = 0xE;
|
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return rev_instructions[2].Generate() | (cond << 28);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
TEST_CASE("Fuzz ARM Load/Store instructions", "[JitX64]") {
|
TEST_CASE("Fuzz ARM Load/Store instructions", "[JitX64]") {
|
||||||
auto forbid_r15 = [](u32 inst) -> bool {
|
auto forbid_r15 = [](u32 inst) -> bool {
|
||||||
return Dynarmic::Common::Bits<12, 15>(inst) != 0b1111;
|
return Dynarmic::Common::Bits<12, 15>(inst) != 0b1111;
|
||||||
|
@ -565,68 +556,39 @@ TEST_CASE("Fuzz ARM Load/Store instructions", "[JitX64]") {
|
||||||
|
|
||||||
SECTION("Doubleword tests") {
|
SECTION("Doubleword tests") {
|
||||||
FuzzJitArm(1, 1, 10000, [&doubleword_instructions]() -> u32 {
|
FuzzJitArm(1, 1, 10000, [&doubleword_instructions]() -> u32 {
|
||||||
u32 cond = 0xE;
|
return doubleword_instructions[RandInt<size_t>(0, doubleword_instructions.size() - 1)].Generate();
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
|
|
||||||
return doubleword_instructions[RandInt<size_t>(0, doubleword_instructions.size() - 1)].Generate() | (cond << 28);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Word tests") {
|
SECTION("Word tests") {
|
||||||
FuzzJitArm(1, 1, 10000, [&word_instructions]() -> u32 {
|
FuzzJitArm(1, 1, 10000, [&word_instructions]() -> u32 {
|
||||||
u32 cond = 0xE;
|
return word_instructions[RandInt<size_t>(0, word_instructions.size() - 1)].Generate();
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return word_instructions[RandInt<size_t>(0, word_instructions.size() - 1)].Generate() | (cond << 28);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Halfword tests") {
|
SECTION("Halfword tests") {
|
||||||
FuzzJitArm(1, 1, 10000, [&halfword_instructions]() -> u32 {
|
FuzzJitArm(1, 1, 10000, [&halfword_instructions]() -> u32 {
|
||||||
u32 cond = 0xE;
|
return halfword_instructions[RandInt<size_t>(0, halfword_instructions.size() - 1)].Generate();
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return halfword_instructions[RandInt<size_t>(0, halfword_instructions.size() - 1)].Generate() | (cond << 28);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Byte tests") {
|
SECTION("Byte tests") {
|
||||||
FuzzJitArm(1, 1, 10000, [&byte_instructions]() -> u32 {
|
FuzzJitArm(1, 1, 10000, [&byte_instructions]() -> u32 {
|
||||||
u32 cond = 0xE;
|
return byte_instructions[RandInt<size_t>(0, byte_instructions.size() - 1)].Generate();
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
return byte_instructions[RandInt<size_t>(0, byte_instructions.size() - 1)].Generate() | (cond << 28);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Mixed tests") {
|
SECTION("Mixed tests") {
|
||||||
FuzzJitArm(10, 10, 10000, [&]() -> u32 {
|
FuzzJitArm(10, 10, 10000, [&]() -> u32 {
|
||||||
size_t selection = RandInt<size_t>(0, 3);
|
switch (RandInt(0, 3)) {
|
||||||
|
|
||||||
u32 cond = 0xE;
|
|
||||||
// Have a one-in-twenty-five chance of actually having a cond.
|
|
||||||
if (RandInt(1, 25) == 1) {
|
|
||||||
cond = RandInt<u32>(0x0, 0xD);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (selection) {
|
|
||||||
case 0:
|
case 0:
|
||||||
return doubleword_instructions[RandInt<size_t>(0, doubleword_instructions.size() - 1)].Generate() | (cond << 28);
|
return doubleword_instructions[RandInt<size_t>(0, doubleword_instructions.size() - 1)].Generate();
|
||||||
case 1:
|
case 1:
|
||||||
return word_instructions[RandInt<size_t>(0, word_instructions.size() - 1)].Generate() | (cond << 28);
|
return word_instructions[RandInt<size_t>(0, word_instructions.size() - 1)].Generate();
|
||||||
case 2:
|
case 2:
|
||||||
return halfword_instructions[RandInt<size_t>(0, halfword_instructions.size() - 1)].Generate() | (cond << 28);
|
return halfword_instructions[RandInt<size_t>(0, halfword_instructions.size() - 1)].Generate();
|
||||||
case 3:
|
case 3:
|
||||||
return byte_instructions[RandInt<size_t>(0, byte_instructions.size() - 1)].Generate() | (cond << 28);
|
return byte_instructions[RandInt<size_t>(0, byte_instructions.size() - 1)].Generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -638,3 +600,39 @@ TEST_CASE("Fuzz ARM Load/Store instructions", "[JitX64]") {
|
||||||
FAIL();
|
FAIL();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") {
|
||||||
|
auto validate_d_m_n = [](u32 inst) -> bool {
|
||||||
|
return Dynarmic::Common::Bits<16, 19>(inst) != 15 &&
|
||||||
|
Dynarmic::Common::Bits<8, 11>(inst) != 15 &&
|
||||||
|
Dynarmic::Common::Bits<0, 3>(inst) != 15;
|
||||||
|
};
|
||||||
|
auto validate_d_a_m_n = [&](u32 inst) -> bool {
|
||||||
|
return validate_d_m_n(inst) &&
|
||||||
|
Dynarmic::Common::Bits<12, 15>(inst) != 15;
|
||||||
|
};
|
||||||
|
auto validate_h_l_m_n = [&](u32 inst) -> bool {
|
||||||
|
return validate_d_a_m_n(inst) &&
|
||||||
|
Dynarmic::Common::Bits<12, 15>(inst) != Dynarmic::Common::Bits<16, 19>(inst);
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<InstructionGenerator, 7> instructions = {
|
||||||
|
{
|
||||||
|
InstructionGenerator("cccc0000001Sddddaaaammmm1001nnnn", validate_d_a_m_n), // MLA
|
||||||
|
InstructionGenerator("cccc0000000Sdddd0000mmmm1001nnnn", validate_d_m_n), // MUL
|
||||||
|
|
||||||
|
InstructionGenerator("cccc0000111Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMLAL
|
||||||
|
InstructionGenerator("cccc0000110Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMULL
|
||||||
|
InstructionGenerator("cccc00000100ddddaaaammmm1001nnnn", validate_h_l_m_n), // UMAAL
|
||||||
|
InstructionGenerator("cccc0000101Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMLAL
|
||||||
|
InstructionGenerator("cccc0000100Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SECTION("Multiply") {
|
||||||
|
FuzzJitArm(2, 2, 10000, [&]() -> u32 {
|
||||||
|
return instructions[RandInt<size_t>(0, instructions.size() - 1)].Generate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue