delete this old retroarch-provided arm-arm jit. nobody can build it. we have officially supported arm jits now, years newer vintage. use those instead.
This commit is contained in:
parent
0346f7b946
commit
76357662be
|
@ -1,276 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include "arm_gen.h"
|
|
||||||
|
|
||||||
#ifdef _3DS
|
|
||||||
# include <malloc.h>
|
|
||||||
# include "3ds/memory.h"
|
|
||||||
#elif defined(VITA)
|
|
||||||
# include <psp2/kernel/sysmem.h>
|
|
||||||
# define RW_INIT sceKernelOpenVMDomain
|
|
||||||
# define RW_END sceKernelCloseVMDomain
|
|
||||||
#else
|
|
||||||
# include <sys/mman.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// __clear_cache(start, end)
|
|
||||||
#ifdef __BLACKBERRY_QNX__
|
|
||||||
#undef __clear_cache
|
|
||||||
#define __clear_cache(start,end) msync(start, (size_t)((void*)end - (void*)start), MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
|
|
||||||
#elif defined(__MACH__)
|
|
||||||
#include <libkern/OSCacheControl.h>
|
|
||||||
#define __clear_cache mach_clear_cache
|
|
||||||
static void __clear_cache(void *start, void *end) {
|
|
||||||
size_t len = (char *)end - (char *)start;
|
|
||||||
sys_dcache_flush(start, len);
|
|
||||||
sys_icache_invalidate(start, len);
|
|
||||||
}
|
|
||||||
#elif defined(_3DS)
|
|
||||||
#undef __clear_cache
|
|
||||||
#define __clear_cache(start,end)FlushInvalidateCache();
|
|
||||||
#elif defined(VITA)
|
|
||||||
#undef __clear_cache
|
|
||||||
#define __clear_cache(start,end)sceKernelSyncVMDomain(block, start, (char *)end - (char *)start)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace arm_gen
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _3DS
|
|
||||||
uint32_t* _instructions = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
code_pool::code_pool(uint32_t icount) :
|
|
||||||
instruction_count(icount),
|
|
||||||
instructions(0),
|
|
||||||
next_instruction(0),
|
|
||||||
flush_start(0)
|
|
||||||
{
|
|
||||||
|
|
||||||
printf("\n\ncode_pool icount: %i\n\n", icount);
|
|
||||||
literal_count = 0;
|
|
||||||
memset(labels, 0, sizeof(labels));
|
|
||||||
memset(branches, 0, sizeof(branches));
|
|
||||||
|
|
||||||
#if defined(_3DS)
|
|
||||||
if(!_instructions)
|
|
||||||
{
|
|
||||||
_instructions = (uint32_t*)memalign(4096, instruction_count * 4);
|
|
||||||
if (!_instructions)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "memalign failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
ReprotectMemory((unsigned int*)_instructions, (instruction_count * 4) / 4096, 7);
|
|
||||||
}
|
|
||||||
instructions = _instructions;
|
|
||||||
#elif defined(VITA)
|
|
||||||
block = sceKernelAllocMemBlockForVM("desmume_rwx_block", instruction_count * 4);
|
|
||||||
if (block < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "sceKernelAllocMemBlockForVM failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceKernelGetMemBlockBase(block, (void **)&instructions) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "sceKernelGetMemBlockBase failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
#elif defined(USE_POSIX_MEMALIGN)
|
|
||||||
if (posix_memalign((void**)&instructions, 4096, instruction_count * 4))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "posix_memalign failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mprotect(instructions, instruction_count * 4, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "mprotect failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
instructions = (uint32_t*)memalign(4096, instruction_count * 4);
|
|
||||||
if (!instructions)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "memalign failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mprotect(instructions, instruction_count * 4, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "mprotect failed\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
code_pool::~code_pool()
|
|
||||||
{
|
|
||||||
#ifdef _3DS
|
|
||||||
//ReprotectMemory((unsigned int*)instructions, (instruction_count * 4) / 4096, 3);
|
|
||||||
#elif defined(VITA)
|
|
||||||
sceKernelFreeMemBlock(block);
|
|
||||||
#else
|
|
||||||
mprotect(instructions, instruction_count * 4, PROT_READ | PROT_WRITE);
|
|
||||||
free(instructions);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void* code_pool::fn_pointer()
|
|
||||||
{
|
|
||||||
void* result = &instructions[flush_start];
|
|
||||||
|
|
||||||
__clear_cache(&instructions[flush_start], &instructions[next_instruction]);
|
|
||||||
flush_start = next_instruction;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::set_label(const char* name)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < TARGET_COUNT; i ++)
|
|
||||||
{
|
|
||||||
if (labels[i].name == name)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Duplicate label\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < TARGET_COUNT; i ++)
|
|
||||||
{
|
|
||||||
if (labels[i].name == 0)
|
|
||||||
{
|
|
||||||
labels[i].name = name;
|
|
||||||
labels[i].position = next_instruction;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Label overflow\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::resolve_label(const char* name)
|
|
||||||
{
|
|
||||||
#ifdef VITA
|
|
||||||
RW_INIT();
|
|
||||||
#endif
|
|
||||||
for (int i = 0; i < TARGET_COUNT; i ++)
|
|
||||||
{
|
|
||||||
if (labels[i].name != name)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < TARGET_COUNT; j ++)
|
|
||||||
{
|
|
||||||
if (branches[j].name != name)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t source = branches[j].position;
|
|
||||||
const uint32_t target = labels[i].position;
|
|
||||||
instructions[source] |= ((target - source) - 2) & 0xFFFFFF;
|
|
||||||
|
|
||||||
branches[j].name = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
labels[i].name = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef VITA
|
|
||||||
RW_END();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code Gen: Generic
|
|
||||||
void code_pool::insert_instruction(uint32_t op, AG_COND cond)
|
|
||||||
{
|
|
||||||
assert(cond < CONDINVALID);
|
|
||||||
insert_raw_instruction((op & 0x0FFFFFFF) | (cond << 28));
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::insert_raw_instruction(uint32_t op)
|
|
||||||
{
|
|
||||||
if (next_instruction >= instruction_count)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "code_pool overflow\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
#ifdef VITA
|
|
||||||
RW_INIT();
|
|
||||||
#endif
|
|
||||||
instructions[next_instruction ++] = op;
|
|
||||||
#ifdef VITA
|
|
||||||
RW_END();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::alu_op(AG_ALU_OP op, reg_t rd, reg_t rn,
|
|
||||||
const alu2& arg, AG_COND cond)
|
|
||||||
{
|
|
||||||
assert(op < OPINVALID);
|
|
||||||
insert_instruction( (op << 20) | (rn << 16) | (rd << 12) | arg.encoding, cond );
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::mem_op(AG_MEM_OP op, reg_t rd, reg_t rn, const mem2& arg,
|
|
||||||
AG_MEM_FLAGS flags, AG_COND cond)
|
|
||||||
{
|
|
||||||
uint32_t instruction = 0x04000000;
|
|
||||||
instruction |= (op & 1) ? 1 << 20 : 0;
|
|
||||||
instruction |= (op & 2) ? 1 << 22 : 0;
|
|
||||||
|
|
||||||
instruction |= arg.encoding;
|
|
||||||
instruction |= rd << 12;
|
|
||||||
instruction |= rn << 16;
|
|
||||||
|
|
||||||
instruction |= flags ^ 0x1800000;
|
|
||||||
|
|
||||||
insert_instruction( instruction, cond );
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::b(const char* target, AG_COND cond)
|
|
||||||
{
|
|
||||||
assert(target);
|
|
||||||
|
|
||||||
for (int i = 0; i < TARGET_COUNT; i ++)
|
|
||||||
{
|
|
||||||
if (branches[i].name == 0)
|
|
||||||
{
|
|
||||||
branches[i].name = target;
|
|
||||||
branches[i].position = next_instruction;
|
|
||||||
insert_instruction( 0x0A000000, cond );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void code_pool::load_constant(reg_t target_reg, uint32_t constant, AG_COND cond)
|
|
||||||
{
|
|
||||||
// TODO: Support another method for ARM procs that don't have movw|movt
|
|
||||||
|
|
||||||
uint32_t instructions[2] = { 0x03000000, 0x03400000 };
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i ++, constant >>= 16)
|
|
||||||
{
|
|
||||||
// If the upper 16-bits are zero the movt op is not needed
|
|
||||||
if (i == 1 && constant == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
instructions[i] |= target_reg << 12;
|
|
||||||
instructions[i] |= constant & 0xFFF;
|
|
||||||
instructions[i] |= (constant & 0xF000) << 4;
|
|
||||||
insert_instruction( instructions[i], cond );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace arm_gen
|
|
|
@ -1,226 +0,0 @@
|
||||||
#ifndef ARM_GEN_H_LR
|
|
||||||
#define ARM_GEN_H_LR
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#if defined(VITA)
|
|
||||||
# include <psp2/kernel/sysmem.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace arm_gen
|
|
||||||
{
|
|
||||||
|
|
||||||
template<uint32_t MAX>
|
|
||||||
struct Constraint
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Constraint(uint32_t val) : value(val) { assert(val < MAX); }
|
|
||||||
operator uint32_t() const { return value; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const uint32_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct reg_t : public Constraint<16>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
reg_t(uint32_t num) : Constraint<16>(num) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Do NOT reorder these enums
|
|
||||||
enum AG_COND
|
|
||||||
{
|
|
||||||
EQ, NE, CS, CC, MI, PL, VS, VC,
|
|
||||||
HI, LS, GE, LT, GT, LE, AL, EGG,
|
|
||||||
CONDINVALID
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AG_ALU_OP
|
|
||||||
{
|
|
||||||
AND, ANDS, EOR, EORS, SUB, SUBS, RSB, RSBS,
|
|
||||||
ADD, ADDS, ADC, ADCS, SBC, SBCS, RSC, RSCS,
|
|
||||||
XX1, TST , XX2, TEQ , XX3, CMP , XX4, CMN ,
|
|
||||||
ORR, ORRS, MOV, MOVS, BIC, BICS, MVN, MVNS,
|
|
||||||
OPINVALID
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AG_MEM_OP
|
|
||||||
{
|
|
||||||
STR, LDR, STRB, LDRB, MEMINVALID
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AG_MEM_FLAGS
|
|
||||||
{
|
|
||||||
POST_INDEX = 1 << 24,
|
|
||||||
NEGATE_OFFSET = 1 << 23,
|
|
||||||
WRITE_BACK = 1 << 21,
|
|
||||||
MEM_NONE = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AG_ALU_SHIFT
|
|
||||||
{
|
|
||||||
LSL, LSR, ASR, ROR, SHIFTINVALID
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alu2
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
alu2(uint32_t val) : encoding(val) { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
static alu2 reg_shift_reg(reg_t rm, AG_ALU_SHIFT type, reg_t rs) { return alu2(rm | (type << 5) | 0x10 | (rs << 8)); }
|
|
||||||
static alu2 reg_shift_imm(reg_t rm, AG_ALU_SHIFT type, uint32_t imm) { return alu2(rm | (type << 5) | (imm << 7)); }
|
|
||||||
static alu2 imm_ror(uint32_t val, uint32_t ror) { return alu2((1 << 25) | ((ror / 2) << 8) | val); }
|
|
||||||
static alu2 imm_rol(uint32_t val, uint32_t rol) { return imm_ror(val, (32 - rol) & 0x1F); }
|
|
||||||
|
|
||||||
|
|
||||||
static alu2 reg(reg_t rm) { return reg_shift_imm(rm, LSL, 0); }
|
|
||||||
static alu2 imm(uint8_t val) { return imm_ror(val, 0); }
|
|
||||||
|
|
||||||
const uint32_t encoding;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mem2
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
mem2(uint32_t val) : encoding(val) { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
static mem2 reg_shift_imm(reg_t rm, AG_ALU_SHIFT type, uint32_t imm) { return mem2((1 << 25) | rm | (type << 5) | (imm << 7)); }
|
|
||||||
|
|
||||||
static mem2 reg(reg_t rm) { return reg_shift_imm(rm, LSL, 0); }
|
|
||||||
static mem2 imm(uint32_t val) { return mem2(val); }
|
|
||||||
|
|
||||||
const uint32_t encoding;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 80 Columns be damned
|
|
||||||
class code_pool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
code_pool(uint32_t instruction_count);
|
|
||||||
~code_pool();
|
|
||||||
|
|
||||||
uint32_t instructions_remaining() const { return instruction_count - next_instruction; }
|
|
||||||
|
|
||||||
void* fn_pointer();
|
|
||||||
|
|
||||||
// Relocs
|
|
||||||
void set_label(const char* name);
|
|
||||||
void resolve_label(const char* name);
|
|
||||||
|
|
||||||
// Code Gen: Generic
|
|
||||||
void insert_instruction(uint32_t op, AG_COND cond = AL);
|
|
||||||
void insert_raw_instruction(uint32_t op);
|
|
||||||
|
|
||||||
// Code Gen: ALU
|
|
||||||
void alu_op(AG_ALU_OP op, reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL);
|
|
||||||
void and_(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(AND , rd, rn, arg, cond); }
|
|
||||||
void and_(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(AND , rd, rd, arg, cond); }
|
|
||||||
void ands(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ANDS, rd, rn, arg, cond); }
|
|
||||||
void ands(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ANDS, rd, rd, arg, cond); }
|
|
||||||
void eor (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(EOR , rd, rn, arg, cond); }
|
|
||||||
void eor (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(EOR , rd, rd, arg, cond); }
|
|
||||||
void eors(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(EORS, rd, rn, arg, cond); }
|
|
||||||
void eors(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(EORS, rd, rd, arg, cond); }
|
|
||||||
void sub (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(SUB , rd, rn, arg, cond); }
|
|
||||||
void sub (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(SUB , rd, rd, arg, cond); }
|
|
||||||
void subs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(SUBS, rd, rn, arg, cond); }
|
|
||||||
void subs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(SUBS, rd, rd, arg, cond); }
|
|
||||||
void rsb (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(RSB , rd, rn, arg, cond); }
|
|
||||||
void rsb (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(RSB , rd, rd, arg, cond); }
|
|
||||||
void rsbs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(RSBS, rd, rn, arg, cond); }
|
|
||||||
void rsbs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(RSBS, rd, rd, arg, cond); }
|
|
||||||
void add (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ADD , rd, rn, arg, cond); }
|
|
||||||
void add (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ADD , rd, rd, arg, cond); }
|
|
||||||
void adds(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ADDS, rd, rn, arg, cond); }
|
|
||||||
void adds(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ADDS, rd, rd, arg, cond); }
|
|
||||||
void adc (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ADC , rd, rn, arg, cond); }
|
|
||||||
void adc (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ADC , rd, rd, arg, cond); }
|
|
||||||
void adcs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ADCS, rd, rn, arg, cond); }
|
|
||||||
void adcs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ADCS, rd, rd, arg, cond); }
|
|
||||||
void sbc (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(SBC , rd, rn, arg, cond); }
|
|
||||||
void sbc (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(SBC , rd, rd, arg, cond); }
|
|
||||||
void sbcs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(SBCS, rd, rn, arg, cond); }
|
|
||||||
void sbcs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(SBCS, rd, rd, arg, cond); }
|
|
||||||
void rsc (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(RSC , rd, rn, arg, cond); }
|
|
||||||
void rsc (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(RSC , rd, rd, arg, cond); }
|
|
||||||
void rscs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(RSCS, rd, rn, arg, cond); }
|
|
||||||
void rscs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(RSCS, rd, rd, arg, cond); }
|
|
||||||
void tst ( reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(TST , rn, rn, arg, cond); } // 1
|
|
||||||
void teq ( reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(TEQ , rn, rn, arg, cond); } // 1
|
|
||||||
void cmp ( reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(CMP , rn, rn, arg, cond); } // 1
|
|
||||||
void cmn ( reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(CMN , rn, rn, arg, cond); } // 1
|
|
||||||
void orr (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ORR , rd, rn, arg, cond); }
|
|
||||||
void orr (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ORR , rd, rd, arg, cond); }
|
|
||||||
void orrs(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(ORRS, rd, rn, arg, cond); }
|
|
||||||
void orrs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(ORRS, rd, rd, arg, cond); }
|
|
||||||
void mov (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(MOV , rd, rd, arg, cond); } // 2
|
|
||||||
void movs(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(MOVS, rd, rd, arg, cond); } // 2
|
|
||||||
void bic (reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(BIC , rd, rn, arg, cond); }
|
|
||||||
void bic (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(BIC , rd, rd, arg, cond); }
|
|
||||||
void bics(reg_t rd, reg_t rn, const alu2& arg, AG_COND cond = AL) { alu_op(BICS, rd, rn, arg, cond); }
|
|
||||||
void bics(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(BICS, rd, rd, arg, cond); }
|
|
||||||
void mvn (reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(MVN , rd, rd, arg, cond); } // 2
|
|
||||||
void mvns(reg_t rd, const alu2& arg, AG_COND cond = AL) { alu_op(MVNS, rd, rd, arg, cond); } // 2
|
|
||||||
|
|
||||||
// Code Gen: Memory
|
|
||||||
void mem_op(AG_MEM_OP op, reg_t rd, reg_t rn, const mem2& arg, AG_MEM_FLAGS flags = MEM_NONE, AG_COND cond = AL);
|
|
||||||
void ldr (reg_t rd, reg_t base, const mem2& arg = mem2::imm(0), AG_MEM_FLAGS flags = MEM_NONE, AG_COND cond = AL) { mem_op(LDR , rd, base, arg, flags, cond); }
|
|
||||||
void str (reg_t rd, reg_t base, const mem2& arg = mem2::imm(0), AG_MEM_FLAGS flags = MEM_NONE, AG_COND cond = AL) { mem_op(STR , rd, base, arg, flags, cond); }
|
|
||||||
void ldrb(reg_t rd, reg_t base, const mem2& arg = mem2::imm(0), AG_MEM_FLAGS flags = MEM_NONE, AG_COND cond = AL) { mem_op(LDRB, rd, base, arg, flags, cond); }
|
|
||||||
void strb(reg_t rd, reg_t base, const mem2& arg = mem2::imm(0), AG_MEM_FLAGS flags = MEM_NONE, AG_COND cond = AL) { mem_op(STRB, rd, base, arg, flags, cond); }
|
|
||||||
|
|
||||||
// Code Gen: Sign Extend
|
|
||||||
void sxtb(reg_t rd, reg_t rm, AG_COND cond = AL) { insert_instruction( 0x06AF0070 | (rd << 12) | rm, cond ); }
|
|
||||||
void sxth(reg_t rd, reg_t rm, AG_COND cond = AL) { insert_instruction( 0x06BF0070 | (rd << 12) | rm, cond ); }
|
|
||||||
void uxtb(reg_t rd, reg_t rm, AG_COND cond = AL) { insert_instruction( 0x06EF0070 | (rd << 12) | rm, cond ); }
|
|
||||||
void uxth(reg_t rd, reg_t rm, AG_COND cond = AL) { insert_instruction( 0x06FF0070 | (rd << 12) | rm, cond ); }
|
|
||||||
|
|
||||||
// Code Gen: Other
|
|
||||||
void set_status(reg_t source_reg, AG_COND cond = AL) { insert_instruction( 0x0128F000 | source_reg, cond ); }
|
|
||||||
void get_status(reg_t dest_reg, AG_COND cond = AL) { insert_instruction( 0x010F0000 | (dest_reg << 12), cond ); }
|
|
||||||
void bx(reg_t target_reg, AG_COND cond = AL) { insert_instruction( 0x012FFF10 | target_reg, cond ); }
|
|
||||||
void blx(reg_t target_reg, AG_COND cond = AL) { insert_instruction( 0x012FFF30 | target_reg, cond ); }
|
|
||||||
void push(uint16_t regs, AG_COND cond = AL) { insert_instruction( 0x092D0000 | regs, cond ); }
|
|
||||||
void pop(uint16_t regs, AG_COND cond = AL) { insert_instruction( 0x08BD0000 | regs, cond ); }
|
|
||||||
|
|
||||||
void b(const char* target, AG_COND cond = AL);
|
|
||||||
|
|
||||||
// Inserts a movw; movt pair to load the constant, omits movt is constant fits in 16 bits.
|
|
||||||
void load_constant(reg_t target_reg, uint32_t constant, AG_COND cond = AL);
|
|
||||||
void insert_constants();
|
|
||||||
|
|
||||||
void jmp(uint32_t offset);
|
|
||||||
void resolve_jmp(uint32_t instruction, uint32_t offset);
|
|
||||||
|
|
||||||
uint32_t get_next_instruction() { return next_instruction; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
const uint32_t instruction_count;
|
|
||||||
uint32_t* instructions;
|
|
||||||
|
|
||||||
uint32_t next_instruction;
|
|
||||||
uint32_t flush_start;
|
|
||||||
|
|
||||||
uint32_t literals[128][2];
|
|
||||||
uint32_t literal_count;
|
|
||||||
|
|
||||||
static const uint32_t TARGET_COUNT = 16;
|
|
||||||
|
|
||||||
struct target
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
uint32_t position;
|
|
||||||
};
|
|
||||||
|
|
||||||
target labels[TARGET_COUNT];
|
|
||||||
target branches[TARGET_COUNT];
|
|
||||||
#if defined(VITA)
|
|
||||||
SceUID block;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
} // namespace arm_gen
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,191 +0,0 @@
|
||||||
#ifndef ARM_JIT_REG_MANAGER_H
|
|
||||||
#define ARM_JIT_REG_MANAGER_H
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "arm_gen.h"
|
|
||||||
#include "armcpu.h"
|
|
||||||
|
|
||||||
extern const arm_gen::reg_t RCPU;
|
|
||||||
|
|
||||||
class register_manager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
register_manager(arm_gen::code_pool* apool) : pool(apool)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
memset(mapping, 0xFF, sizeof(mapping));
|
|
||||||
memset(usage_tag, 0, sizeof(usage_tag));
|
|
||||||
memset(dirty, 0, sizeof(dirty));
|
|
||||||
memset(weak, 0, sizeof(weak));
|
|
||||||
next_usage_tag = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_usable(arm_gen::reg_t reg) const
|
|
||||||
{
|
|
||||||
static const uint32_t USE_MAP = 0xDE0;
|
|
||||||
return (USE_MAP & (1 << reg)) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int32_t find(uint32_t emu_reg_id)
|
|
||||||
{
|
|
||||||
for (int i = 0; i != 16; i ++)
|
|
||||||
{
|
|
||||||
if (is_usable(i) && mapping[i] == emu_reg_id)
|
|
||||||
{
|
|
||||||
usage_tag[i] = next_usage_tag ++;
|
|
||||||
assert(is_usable(i));
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t get_loaded(uint32_t emu_reg_id, bool no_read)
|
|
||||||
{
|
|
||||||
int32_t current = find(emu_reg_id);
|
|
||||||
|
|
||||||
if (current >= 0)
|
|
||||||
{
|
|
||||||
if (weak[current] && !no_read)
|
|
||||||
{
|
|
||||||
read_emu(current, emu_reg_id);
|
|
||||||
weak[current] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
arm_gen::reg_t get_oldest()
|
|
||||||
{
|
|
||||||
uint32_t result = 0;
|
|
||||||
uint32_t lowtag = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
for (int i = 0; i != 16; i ++)
|
|
||||||
{
|
|
||||||
if (is_usable(i) && usage_tag[i] < lowtag)
|
|
||||||
{
|
|
||||||
lowtag = usage_tag[i];
|
|
||||||
result = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(is_usable(result));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void get(uint32_t reg_count, int32_t* emu_reg_ids)
|
|
||||||
{
|
|
||||||
assert(reg_count < 5);
|
|
||||||
bool found[5] = { false, false, false, false, false };
|
|
||||||
|
|
||||||
// Find existing registers
|
|
||||||
for (uint32_t i = 0; i < reg_count; i ++)
|
|
||||||
{
|
|
||||||
if (emu_reg_ids[i] < 0)
|
|
||||||
{
|
|
||||||
found[i] = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int32_t current = get_loaded(emu_reg_ids[i] & 0xF, emu_reg_ids[i] & 0x10);
|
|
||||||
if (current >= 0)
|
|
||||||
{
|
|
||||||
emu_reg_ids[i] = current;
|
|
||||||
found[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load new registers
|
|
||||||
for (uint32_t i = 0; i != reg_count; i ++)
|
|
||||||
{
|
|
||||||
if (!found[i])
|
|
||||||
{
|
|
||||||
// Search register list again, in case the same register is used twice
|
|
||||||
int32_t current = get_loaded(emu_reg_ids[i] & 0xF, emu_reg_ids[i] & 0x10);
|
|
||||||
if (current >= 0)
|
|
||||||
{
|
|
||||||
emu_reg_ids[i] = current;
|
|
||||||
found[i] = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Read the new register
|
|
||||||
arm_gen::reg_t result = get_oldest();
|
|
||||||
flush(result);
|
|
||||||
|
|
||||||
if (!(emu_reg_ids[i] & 0x10))
|
|
||||||
{
|
|
||||||
read_emu(result, emu_reg_ids[i] & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping[result] = emu_reg_ids[i] & 0xF;
|
|
||||||
usage_tag[result] = next_usage_tag ++;
|
|
||||||
weak[result] = (emu_reg_ids[i] & 0x10) ? true : false;
|
|
||||||
|
|
||||||
emu_reg_ids[i] = result;
|
|
||||||
found[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mark_dirty(uint32_t native_reg)
|
|
||||||
{
|
|
||||||
assert(is_usable(native_reg));
|
|
||||||
dirty[native_reg] = true;
|
|
||||||
weak[native_reg] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush(uint32_t native_reg)
|
|
||||||
{
|
|
||||||
assert(is_usable(native_reg));
|
|
||||||
if (dirty[native_reg] && !weak[native_reg])
|
|
||||||
{
|
|
||||||
write_emu(native_reg, mapping[native_reg]);
|
|
||||||
dirty[native_reg] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_all()
|
|
||||||
{
|
|
||||||
for (int i = 0; i != 16; i ++)
|
|
||||||
{
|
|
||||||
if (is_usable(i))
|
|
||||||
{
|
|
||||||
flush(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void read_emu(arm_gen::reg_t native, arm_gen::reg_t emu)
|
|
||||||
{
|
|
||||||
pool->ldr(native, RCPU, arm_gen::mem2::imm(offsetof(armcpu_t, R) + 4 * emu));
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_emu(arm_gen::reg_t native, arm_gen::reg_t emu)
|
|
||||||
{
|
|
||||||
pool->str(native, RCPU, arm_gen::mem2::imm(offsetof(armcpu_t, R) + 4 * emu));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
arm_gen::code_pool* pool;
|
|
||||||
|
|
||||||
uint32_t mapping[16]; // Mapping[native] = emu
|
|
||||||
uint32_t usage_tag[16];
|
|
||||||
bool dirty[16];
|
|
||||||
bool weak[16];
|
|
||||||
|
|
||||||
uint32_t next_usage_tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue