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