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:
zeromus 2022-05-22 01:09:59 -04:00
parent 0346f7b946
commit 76357662be
4 changed files with 0 additions and 2240 deletions

View File

@ -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

View File

@ -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

View File

@ -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