Line 286... |
Line 286... |
|
|
static int sigsegv_state = 0;
|
static int sigsegv_state = 0;
|
static void *sigsegv_addr = NULL;
|
static void *sigsegv_addr = NULL;
|
|
|
void dyn_ret_stack_prot(void);
|
void dyn_ret_stack_prot(void);
|
void dump_held_xrefs(struct dyn_page *dp, FILE *f);
|
|
|
|
void dyn_sigsegv_debug(int u, siginfo_t *siginf, void *dat)
|
void dyn_sigsegv_debug(int u, siginfo_t *siginf, void *dat)
|
{
|
{
|
struct dyn_page *dp;
|
struct dyn_page *dp;
|
FILE *f;
|
FILE *f;
|
Line 335... |
Line 334... |
|
|
fclose(f);
|
fclose(f);
|
}
|
}
|
sigsegv_state++;
|
sigsegv_state++;
|
case 2:
|
case 2:
|
/* Dump the x-refs to disk */
|
|
for(dp = cpu_state.dyn_pages; dp; dp = dp->next) {
|
|
printf("Dumping cross references of 0x%"PRIxADDR" to disk\n", dp->or_page);
|
|
|
|
sprintf(filen, "or_xref.%"PRIxADDR, dp->or_page);
|
|
if(!(f = fopen(filen, "w"))) {
|
|
fprintf(stderr, "Unable to open %s to dump cross references to: %s\n",
|
|
filen, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
fprintf(f, "Cross references in the page:\n");
|
|
dump_xrefs(dp, f);
|
|
|
|
fprintf(f, "\nCross references held by this page:\n");
|
|
dump_held_xrefs(dp, f);
|
|
|
|
fclose(f);
|
|
}
|
|
sigsegv_state++;
|
|
case 3:
|
|
/* Dump the contents of the stack */
|
/* Dump the contents of the stack */
|
printf("Stack dump: ");
|
printf("Stack dump: ");
|
fflush(stdout);
|
fflush(stdout);
|
|
|
num_trace = backtrace(trace, 10);
|
num_trace = backtrace(trace, 10);
|
Line 378... |
Line 356... |
}
|
}
|
printf("\n");
|
printf("\n");
|
fflush(stdout);
|
fflush(stdout);
|
}
|
}
|
sigsegv_state++;
|
sigsegv_state++;
|
case 4:
|
case 3:
|
sim_done();
|
sim_done();
|
}
|
}
|
}
|
}
|
|
|
void dump_xrefs(struct dyn_page *dp, FILE *f)
|
|
{
|
|
struct x_ref *xref;
|
|
|
|
fprintf(f, "--- Cross reference dump for %"PRIxADDR" at %p ---\n",
|
|
dp->or_page, dp->host_page);
|
|
for(xref = dp->xrefs; xref; xref = xref->next) {
|
|
fprintf(f, "x-refed or location: 0x%"PRIxADDR", host-location: %p, ref: %i\n",
|
|
xref->or_addr, xref->dyn_addr, xref->ref);
|
|
}
|
|
fprintf(f, "--- Cross reference dump end ---\n");
|
|
}
|
|
|
|
void dump_held_xrefs(struct dyn_page *dp, FILE *f)
|
|
{
|
|
struct x_ref **xrefs;
|
|
|
|
fprintf(f, "--- Held cross reference dump for %"PRIxADDR" at %p ---\n",
|
|
dp->or_page, dp->host_page);
|
|
for(xrefs = dp->held_xrefs; *xrefs; xrefs++)
|
|
fprintf(f, "Holds an x-ref to 0x%"PRIxADDR", host-location: %p, ref: %i\n",
|
|
(*xrefs)->or_addr, (*xrefs)->dyn_addr, (*xrefs)->ref);
|
|
fprintf(f, "--- Held cross reference dump end ---\n");
|
|
}
|
|
|
|
static void add_to_dp(struct dyn_page *new)
|
static void add_to_dp(struct dyn_page *new)
|
{
|
{
|
struct dyn_page *cur;
|
struct dyn_page *cur;
|
struct dyn_page *prev;
|
struct dyn_page *prev;
|
|
|
Line 431... |
Line 384... |
struct dyn_page *new_dp(oraddr_t page)
|
struct dyn_page *new_dp(oraddr_t page)
|
{
|
{
|
struct dyn_page *dp = malloc(sizeof(struct dyn_page));
|
struct dyn_page *dp = malloc(sizeof(struct dyn_page));
|
dp->or_page = ADDR_PAGE(page);
|
dp->or_page = ADDR_PAGE(page);
|
|
|
/* Allocate xref terminator */
|
dp->locs = malloc(sizeof(void *) * (PAGE_LEN / 4));
|
dp->xrefs = NULL;
|
|
|
|
dp->held_xrefs = malloc(sizeof(struct x_ref *));
|
|
dp->held_xrefs[0] = NULL;
|
|
|
|
dp->host_len = 0;
|
dp->host_len = 0;
|
dp->host_page = NULL;
|
dp->host_page = NULL;
|
dp->dirty = 1;
|
dp->dirty = 1;
|
|
|
Line 460... |
Line 409... |
cur = cur->next;
|
cur = cur->next;
|
}
|
}
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Finds the dynamicly recompiled location of the given or address */
|
|
struct x_ref *find_host_x_ref(struct x_ref *x_refs, oraddr_t addr)
|
|
{
|
|
/* FIXME: Optimise this by knowing that the x_refs array is orderd */
|
|
while(x_refs && (x_refs->or_addr != addr)) x_refs = x_refs->next;
|
|
|
|
return x_refs;
|
|
}
|
|
|
|
static void remove_xref(struct dyn_page *dp, struct x_ref *xref)
|
|
{
|
|
struct x_ref *prev_xref;
|
|
|
|
if(dp->xrefs == xref) {
|
|
dp->xrefs = xref->next;
|
|
free(xref);
|
|
return;
|
|
}
|
|
|
|
prev_xref = dp->xrefs;
|
|
while(prev_xref->next != xref)
|
|
prev_xref = prev_xref->next;
|
|
|
|
prev_xref->next = xref->next;
|
|
free(xref);
|
|
}
|
|
|
|
struct x_ref *find_held_x_ref(struct x_ref **held_xrefs, oraddr_t or_addr)
|
|
{
|
|
/* FIXME: Order this list in add_to_held_xrefs below and optimise this */
|
|
while(*held_xrefs && ((*held_xrefs)->or_addr != or_addr)) held_xrefs++;
|
|
return *held_xrefs;
|
|
}
|
|
|
|
void add_to_held_xrefs(struct dyn_page *dp, struct x_ref *xref)
|
|
{
|
|
unsigned int i;
|
|
|
|
for(i = 0; dp->held_xrefs[i]; i++);
|
|
|
|
dp->held_xrefs = realloc(dp->held_xrefs, sizeof(struct x_ref *) * (i + 2));
|
|
dp->held_xrefs[i] = xref;
|
|
dp->held_xrefs[++i] = NULL;
|
|
}
|
|
|
|
/* This is called whenever the immu is either enabled/disabled or reconfigured
|
/* This is called whenever the immu is either enabled/disabled or reconfigured
|
* while enabled. This checks if an itlb miss would occour and updates the immu
|
* while enabled. This checks if an itlb miss would occour and updates the immu
|
* hit delay counter */
|
* hit delay counter */
|
void recheck_immu(int got_en_dis)
|
void recheck_immu(int got_en_dis)
|
{
|
{
|
Line 561... |
Line 465... |
{
|
{
|
int brk;
|
int brk;
|
oraddr_t pc = get_pc();
|
oraddr_t pc = get_pc();
|
|
|
if(!cpu_state.ts_current)
|
if(!cpu_state.ts_current)
|
upd_reg_from_t(pc);
|
upd_reg_from_t(pc, 0);
|
|
|
if(add_normal && do_stats) {
|
if(add_normal && do_stats) {
|
cpu_state.iqueue.insn_addr = pc;
|
cpu_state.iqueue.insn_addr = pc;
|
cpu_state.iqueue.insn = eval_insn_direct(pc, &brk, 1);
|
cpu_state.iqueue.insn = eval_insn_direct(pc, &brk, 1);
|
cpu_state.iqueue.insn_index = insn_decode(cpu_state.iqueue.insn);
|
cpu_state.iqueue.insn_index = insn_decode(cpu_state.iqueue.insn);
|
|
runtime.cpu.instructions++;
|
analysis(&cpu_state.iqueue);
|
analysis(&cpu_state.iqueue);
|
}
|
}
|
|
|
/* Run the scheduler */
|
/* Run the scheduler */
|
if(add_normal)
|
if(add_normal)
|
Line 583... |
Line 488... |
}
|
}
|
|
|
/* Signals a page as dirty */
|
/* Signals a page as dirty */
|
void dirtyfy_page(struct dyn_page *dp)
|
void dirtyfy_page(struct dyn_page *dp)
|
{
|
{
|
struct x_ref **held_xrefs;
|
|
struct x_ref *xref;
|
|
oraddr_t check;
|
oraddr_t check;
|
|
|
printf("Dirtyfying page 0x%"PRIxADDR"\n", dp->or_page);
|
printf("Dirtyfying page 0x%"PRIxADDR"\n", dp->or_page);
|
|
|
/* decrease the reference counts of the xrefs that we hold */
|
|
for(held_xrefs = dp->held_xrefs; *held_xrefs; held_xrefs++)
|
|
(*held_xrefs)->ref--;
|
|
dp->held_xrefs = realloc(dp->held_xrefs, sizeof(struct x_ref *));
|
|
dp->held_xrefs[0] = NULL;
|
|
|
|
dp->dirty = 1;
|
dp->dirty = 1;
|
|
|
/* If the execution is currently in the page that was touched then recompile
|
/* If the execution is currently in the page that was touched then recompile
|
* it now and jump back to the point of execution */
|
* it now and jump back to the point of execution */
|
check = cpu_state.delay_insn ? cpu_state.pc_delay : get_pc() + 4;
|
check = cpu_state.delay_insn ? cpu_state.pc_delay : get_pc() + 4;
|
if(ADDR_PAGE(check) == dp->or_page) {
|
if(ADDR_PAGE(check) == dp->or_page) {
|
run_sched_out_of_line(1);
|
run_sched_out_of_line(1);
|
if(!(xref = find_host_x_ref(dp->xrefs, check))) {
|
|
xref = add_to_xrefs(dp, check);
|
|
add_to_held_xrefs(dp, xref);
|
|
} else {
|
|
if(!find_held_x_ref(dp->held_xrefs, check)) {
|
|
add_to_held_xrefs(dp, xref);
|
|
xref->ref++;
|
|
}
|
|
}
|
|
recompile_page(dp);
|
recompile_page(dp);
|
|
|
cpu_state.delay_insn = 0;
|
cpu_state.delay_insn = 0;
|
|
|
/* Jump out to the next instruction */
|
/* Jump out to the next instruction */
|
or_longjmp(xref->dyn_addr);
|
do_jump(check);
|
}
|
}
|
}
|
}
|
|
|
static void ship_gprs_out_t(struct op_queue *opq, int end, unsigned int *reg_t)
|
static void ship_gprs_out_t(struct op_queue *opq, int end, unsigned int *reg_t)
|
{
|
{
|
int i;
|
int i;
|
|
|
|
/* Before takeing the temporaries out, temporarily remove the op_do_sched
|
|
* operation such that dyn_page->ts_bound shall be correct before the
|
|
* scheduler runs */
|
|
if(end && opq->num_ops && (opq->ops[opq->num_ops - 1] == op_do_sched_indx)) {
|
|
opq->num_ops--;
|
|
ship_gprs_out_t(opq, end, reg_t);
|
|
gen_op_do_sched(opq, 1);
|
|
return;
|
|
}
|
|
|
for(i = 0; i < NUM_T_REGS; i++) {
|
for(i = 0; i < NUM_T_REGS; i++) {
|
if(reg_t[i] < 32)
|
if(reg_t[i] < 32)
|
gen_op_move_gpr_t[i][reg_t[i]](opq, end);
|
gen_op_move_gpr_t[i][reg_t[i]](opq, end);
|
}
|
}
|
}
|
}
|
Line 690... |
Line 588... |
}
|
}
|
|
|
opq->num_ops++;
|
opq->num_ops++;
|
}
|
}
|
|
|
|
static void gen_op_mark_loc(struct op_queue *opq, int end)
|
|
{
|
|
add_to_opq(opq, end, op_mark_loc_indx);
|
|
}
|
|
|
/* Adds a parameter to the opq */
|
/* Adds a parameter to the opq */
|
void add_to_op_params(struct op_queue *opq, int end, unsigned long param)
|
void add_to_op_params(struct op_queue *opq, int end, unsigned long param)
|
{
|
{
|
if(opq->num_ops_param == opq->ops_param_len) {
|
if(opq->num_ops_param == opq->ops_param_len) {
|
opq->ops_param_len += OPS_ENLARGE_BY * sizeof(int);
|
opq->ops_param_len += OPS_ENLARGE_BY;
|
if(!(opq->ops_param = realloc(opq->ops_param, opq->ops_param_len))) {
|
if(!(opq->ops_param = realloc(opq->ops_param, opq->ops_param_len * sizeof(int)))) {
|
fprintf(stderr, "OOM\n");
|
fprintf(stderr, "OOM\n");
|
exit(1);
|
exit(1);
|
}
|
}
|
}
|
}
|
|
|
Line 733... |
Line 636... |
/* Initialises the recompiler */
|
/* Initialises the recompiler */
|
void init_dyn_recomp(void)
|
void init_dyn_recomp(void)
|
{
|
{
|
struct sigaction sigact;
|
struct sigaction sigact;
|
struct op_queue *opq;
|
struct op_queue *opq;
|
struct x_ref *xref;
|
|
unsigned int i;
|
unsigned int i;
|
|
|
cpu_state.opqs = NULL;
|
cpu_state.opqs = NULL;
|
|
|
/* Allocate the operation queue list (+1 for the page chaining) */
|
/* Allocate the operation queue list (+1 for the page chaining) */
|
Line 750... |
Line 652... |
/* initialise some fields */
|
/* initialise some fields */
|
opq->ops_len = 0;
|
opq->ops_len = 0;
|
opq->ops = NULL;
|
opq->ops = NULL;
|
opq->ops_param_len = 0;
|
opq->ops_param_len = 0;
|
opq->ops_param = NULL;
|
opq->ops_param = NULL;
|
|
opq->xref = 0;
|
|
|
if(cpu_state.opqs)
|
if(cpu_state.opqs)
|
cpu_state.opqs->prev = opq;
|
cpu_state.opqs->prev = opq;
|
|
|
opq->next = cpu_state.opqs;
|
opq->next = cpu_state.opqs;
|
cpu_state.opqs = opq;
|
cpu_state.opqs = opq;
|
}
|
}
|
|
|
opq->prev = NULL;
|
opq->prev = NULL;
|
|
|
/* Allocate the x-ref structures that will be used for the infinite loop
|
|
* instruction (l.j 0). Allocate a whole page's worth just to make sure that
|
|
* we will have enough */
|
|
for(i = 0; i < (PAGE_LEN / 4); i++) {
|
|
if(!(xref = malloc(sizeof(struct x_ref)))) {
|
|
fprintf(stderr, "Out-of-memory while allocateing x-ref structures\n");
|
|
exit(1);
|
|
}
|
|
xref->next = cpu_state.inf_xrefs;
|
|
cpu_state.inf_xrefs = xref;
|
|
}
|
|
|
|
/* Just some value that we'll use as the base for our stack */
|
/* Just some value that we'll use as the base for our stack */
|
rec_stack_base = get_sp();
|
rec_stack_base = get_sp();
|
|
|
cpu_state.curr_page = NULL;
|
cpu_state.curr_page = NULL;
|
cpu_state.dyn_pages = NULL;
|
cpu_state.dyn_pages = NULL;
|
Line 785... |
Line 676... |
memset(&sigact.sa_mask, 0, sizeof(sigact.sa_mask));
|
memset(&sigact.sa_mask, 0, sizeof(sigact.sa_mask));
|
sigact.sa_flags = SA_SIGINFO | SA_NOMASK;
|
sigact.sa_flags = SA_SIGINFO | SA_NOMASK;
|
if(sigaction(SIGSEGV, &sigact, NULL))
|
if(sigaction(SIGSEGV, &sigact, NULL))
|
printf("WARN: Unable to install SIGSEGV handler! Don't expect to be able to debug the recompiler.\n");
|
printf("WARN: Unable to install SIGSEGV handler! Don't expect to be able to debug the recompiler.\n");
|
|
|
/* Allocate memory for the rfe corss reference cache */
|
/* Do architecture specific initialisation */
|
if(!(cpu_state.rfe_held_xrefs = malloc(sizeof(struct xref *) * NUM_RFE_HELD))) {
|
|
printf("OOM\n");
|
|
exit(1);
|
|
}
|
|
cpu_state.rfe_held_xref_pos = 0;
|
|
memset(cpu_state.rfe_held_xrefs, 0, sizeof(struct xref *) * NUM_RFE_HELD);
|
|
|
|
init_dyn_rec();
|
init_dyn_rec();
|
|
|
/* FIXME: Find a better place for this */
|
/* FIXME: Find a better place for this */
|
{ /* Needed by execution */
|
{ /* Needed by execution */
|
extern int do_stats;
|
extern int do_stats;
|
Line 805... |
Line 689... |
}
|
}
|
|
|
printf("Recompile engine up and running\n");
|
printf("Recompile engine up and running\n");
|
}
|
}
|
|
|
/* rec_page is a physical address */
|
/* Adds code to the opq for the instruction pointed to by addr */
|
void recompile_page(struct dyn_page *dyn)
|
static void recompile_insn(struct op_queue *opq, oraddr_t addr, int delay_insn)
|
{
|
{
|
unsigned int j, k;
|
|
unsigned int reg_t[NUM_T_REGS];
|
|
unsigned int pres_t[NUM_T_REGS]; /* Which temporary to preserve */
|
|
unsigned int insn_index;
|
unsigned int insn_index;
|
int delay_insn = 0; /* Is the next instruction to be decoded in a delay slot*/
|
unsigned int pres_t[NUM_T_REGS]; /* Which temporary to preserve */
|
enum insn_type delay_insn_type = 0;
|
orreg_t param[3];
|
uint32_t insn;
|
int i, j, k;
|
int param_t[3]; /* Which temporary the parameters reside in */
|
int param_t[3]; /* Which temporary the parameters reside in */
|
int param_r[3]; /* is parameter a register */
|
int param_r[3]; /* is parameter a register */
|
orreg_t param[3];
|
|
int param_num;
|
int param_num;
|
struct op_queue *opq = NULL;
|
uint32_t insn;
|
oraddr_t rec_addr = dyn->or_page;
|
|
oraddr_t rec_page = dyn->or_page;
|
|
struct x_ref *xref;
|
|
int breakp;
|
int breakp;
|
struct dyn_page *prev_dp;
|
|
|
|
struct insn_op_struct *opd;
|
struct insn_op_struct *opd;
|
|
|
/* The start of the next page */
|
|
rec_page += PAGE_LEN;
|
|
|
|
printf("Recompileing page %"PRIxADDR"\n", rec_addr);
|
|
fflush(stdout);
|
|
|
|
/* Mark all temporaries as not containing a register */
|
|
for(j = 0; j < NUM_T_REGS; j++)
|
|
reg_t[j] = 32; /* Out-of-range registers */
|
|
|
|
dyn->delayr = -verify_memoryarea(rec_addr)->delayr;
|
|
|
|
dyn->carrys_delay_slot = 0;
|
|
|
|
/* Check if the previous page carries a delay slot over to this page */
|
|
if((prev_dp = find_dynd_page(rec_addr - PAGE_LEN)))
|
|
delay_insn = prev_dp->carrys_delay_slot;
|
|
|
|
for(opq = cpu_state.opqs; rec_addr < rec_page; rec_addr += 4, opq = opq->next) {
|
|
opq->num_ops = 0;
|
|
opq->num_ops_param = 0;
|
|
opq->jump_local = 0;
|
|
|
|
opq->insn_addr = rec_addr;
|
|
|
|
breakp = 0;
|
breakp = 0;
|
insn = eval_insn(rec_addr, &breakp);
|
insn = eval_insn(addr, &breakp);
|
|
|
/* FIXME: If a breakpoint is set at this location, insert exception code */
|
/* FIXME: If a breakpoint is set at this location, insert exception code */
|
if(breakp) {
|
if(breakp) {
|
fprintf(stderr, "FIXME: Insert breakpoint code\n");
|
fprintf(stderr, "FIXME: Insert breakpoint code\n");
|
}
|
}
|
|
|
insn_index = insn_decode(insn);
|
insn_index = insn_decode(insn);
|
|
|
/* FIXME: Optimise this by knowing that dyn->x_refs is ordered (ie. Don't
|
/* Copy over the state of the temporaries to the next opq */
|
* call find_host_x_ref) */
|
memcpy(opq->reg_t_d, opq->reg_t, sizeof(opq->reg_t));
|
/* Check if this location is cross referenced */
|
|
if((xref = find_host_x_ref(dyn->xrefs, rec_addr))) {
|
|
/* If the x-refs reference count reached zero remove it */
|
|
if(xref->ref) {
|
|
/* If the current address is cross-referenced, the temporaries shall be
|
|
* in an undefined state, so we must assume that no registers reside in
|
|
* them */
|
|
/* Ship out the current set of registers from the temporaries */
|
|
if(opq->prev) {
|
|
ship_gprs_out_t(opq->prev, 1, reg_t);
|
|
}
|
|
for(j = 0; j < NUM_T_REGS; j++)
|
|
reg_t[j] = 32;
|
|
} else {
|
|
/* Remove x-ref */
|
|
remove_xref(dyn, xref);
|
|
}
|
|
}
|
|
|
|
memcpy(opq->reg_t, reg_t, sizeof(reg_t));
|
|
|
|
/* Check if we have an illegal instruction */
|
/* Check if we have an illegal instruction */
|
if(insn_index == -1) {
|
if(insn_index == -1) {
|
gen_l_invalid(opq, param_t, param, delay_insn);
|
gen_l_invalid(opq, NULL, NULL, delay_insn);
|
if(delay_insn) {
|
return;
|
/* There is no need to do any jump handleing stuff as the instruction
|
|
* will generate an exception */
|
|
if(opq->prev->jump_local == 2) {
|
|
opq->prev->xref->next = cpu_state.inf_xrefs;
|
|
cpu_state.inf_xrefs = opq->prev->xref;
|
|
}
|
|
opq->prev->jump_local = 0;
|
|
delay_insn = 0;
|
|
}
|
|
continue;
|
|
}
|
}
|
|
|
|
/* If we are recompileing an instruction that has a delay slot and is in the
|
|
* delay slot, ignore it. This is undefined behavour. */
|
|
if(delay_insn && ((or32_opcodes[insn_index].func_unit == it_jump) ||
|
|
(or32_opcodes[insn_index].func_unit == it_branch)))
|
|
return;
|
|
|
/* figure out instruction operands */
|
/* figure out instruction operands */
|
for(j = 0; j < NUM_T_REGS; j++)
|
for(i = 0; i < NUM_T_REGS; i++)
|
pres_t[j] = 0;
|
pres_t[i] = 0;
|
|
|
param_t[0] = T_NONE;
|
param_t[0] = T_NONE;
|
param_t[1] = T_NONE;
|
param_t[1] = T_NONE;
|
param_t[2] = T_NONE;
|
param_t[2] = T_NONE;
|
param_r[0] = 0;
|
param_r[0] = 0;
|
Line 920... |
Line 746... |
while(1) {
|
while(1) {
|
param[param_num] = eval_operand_val(insn, opd);
|
param[param_num] = eval_operand_val(insn, opd);
|
|
|
if(opd->type & OPTYPE_REG) {
|
if(opd->type & OPTYPE_REG) {
|
/* check which temporary the register is in, if any */
|
/* check which temporary the register is in, if any */
|
for(j = 0; j < NUM_T_REGS; j++) {
|
for(i = 0; i < NUM_T_REGS; i++) {
|
if(reg_t[j] == param[param_num]) {
|
if(opq->reg_t_d[i] == param[param_num]) {
|
param_t[param_num] = j;
|
param_t[param_num] = i;
|
pres_t[j] = 1;
|
pres_t[i] = 1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
param_num++;
|
param_num++;
|
Line 935... |
Line 761... |
if(opd->type & OPTYPE_LAST)
|
if(opd->type & OPTYPE_LAST)
|
break;
|
break;
|
opd++;
|
opd++;
|
}
|
}
|
|
|
opd = op_start[insn_index];
|
/* Jump instructions are special since they have a delay slot and thus they
|
|
* need to control the exact operation sequence. Special case these, here to
|
|
* avoid haveing loads of if(.func_unit != it_jump && != it_branch) below */
|
|
if((or32_opcodes[insn_index].func_unit == it_jump) ||
|
|
(or32_opcodes[insn_index].func_unit == it_branch)) {
|
|
/* Ship the jump-to register out (if it exists). It requires special
|
|
* handleing, which is done in gen_j_reg. */
|
|
for(i = 0; i < NUM_T_REGS; i++) {
|
|
if(pres_t[i]) {
|
|
gen_op_move_gpr_t[i][opq->reg_t_d[i]](opq->prev, 1);
|
|
opq->reg_t_d[i] = 32;
|
|
opq->reg_t[i] = 32;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Do this in a more elegent way */
|
|
if(!strncmp(or32_opcodes[insn_index].name, "l.jal", 5)) {
|
|
/* In the case of a l.jal instruction, make sure that LINK_REGNO is not in
|
|
* a temporary. The problem is that the l.jal(r) instruction stores the
|
|
* `return address' in LINK_REGNO. The temporaries are shiped out only
|
|
* after the delay slot instruction has executed and so it overwrittes the
|
|
* `return address'. */
|
|
for(k = 0; k < NUM_T_REGS; k++) {
|
|
if(opq->reg_t_d[k] == LINK_REGNO) {
|
|
gen_op_move_gpr_t[k][LINK_REGNO](opq, 1);
|
|
opq->reg_t_d[k] = 32;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Jump instructions don't have a disposition */
|
|
or32_opcodes[insn_index].exec(opq, param_t, param, delay_insn);
|
|
|
|
/* Analysis is done by the individual jump instructions */
|
|
/* Jump instructions don't touch runtime.sim.mem_cycles */
|
|
/* Jump instructions run their own scheduler */
|
|
return;
|
|
}
|
|
|
/* Before an exception takes place, all registers must be stored. */
|
/* Before an exception takes place, all registers must be stored. */
|
if((or32_opcodes[insn_index].func_unit == it_exception)) {
|
if((or32_opcodes[insn_index].func_unit == it_exception)) {
|
if(opq->prev) {
|
if(opq->prev) {
|
ship_gprs_out_t(opq->prev, 1, reg_t);
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t_d);
|
for(j = 0; j < NUM_T_REGS; j++) {
|
for(i = 0; i < NUM_T_REGS; i++) {
|
opq->prev->reg_t[j] = 32;
|
opq->reg_t_d[i] = 32;
|
reg_t[j] = 32;
|
opq->reg_t[i] = 32;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
opd = op_start[insn_index];
|
|
|
for(j = 0; j < param_num; j++, opd++) {
|
for(j = 0; j < param_num; j++, opd++) {
|
while(!(opd->type & OPTYPE_OP)) opd++;
|
while(!(opd->type & OPTYPE_OP)) opd++;
|
if(!(opd->type & OPTYPE_REG))
|
if(!(opd->type & OPTYPE_REG))
|
continue;
|
continue;
|
|
|
Line 960... |
Line 826... |
continue;
|
continue;
|
|
|
/* Check if this register has been moved into a temporary in a previous
|
/* Check if this register has been moved into a temporary in a previous
|
* operand */
|
* operand */
|
for(k = 0; k < NUM_T_REGS; k++) {
|
for(k = 0; k < NUM_T_REGS; k++) {
|
if(reg_t[k] == param[j]) {
|
if(opq->reg_t_d[k] == param[j]) {
|
/* Yes, this register is already in a temporary */
|
/* Yes, this register is already in a temporary */
|
|
if(or32_opcodes[insn_index].func_unit != it_jump) {
|
pres_t[k] = 1;
|
pres_t[k] = 1;
|
reg_t[k] = param[j];
|
|
param_t[j] = k;
|
param_t[j] = k;
|
|
}
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if(k != NUM_T_REGS)
|
if(k != NUM_T_REGS)
|
continue;
|
continue;
|
|
|
if((param_t[j] != T_NONE))
|
if(param_t[j] != T_NONE)
|
continue;
|
continue;
|
|
|
/* Search for an unused temporary */
|
/* Search for an unused temporary */
|
k = find_unused_t(pres_t, reg_t);
|
k = find_unused_t(pres_t, opq->reg_t_d);
|
if(reg_t[k] < 32) {
|
if(opq->reg_t_d[k] < 32) {
|
gen_op_move_gpr_t[k][reg_t[k]](opq->prev, 1);
|
/* FIXME: Only ship the temporary out if it has been used as a destination
|
|
* register */
|
|
gen_op_move_gpr_t[k][opq->reg_t_d[k]](opq->prev, 1);
|
opq->reg_t[k] = 32;
|
opq->reg_t[k] = 32;
|
|
opq->reg_t_d[k] = 32;
|
}
|
}
|
pres_t[k] = 1;
|
pres_t[k] = 1;
|
reg_t[k] = param[j];
|
opq->reg_t_d[k] = param[j];
|
param_t[j] = k;
|
param_t[j] = k;
|
/* FIXME: Only generate code to move the register into a temporary if it
|
/* FIXME: Only generate code to move the register into a temporary if it
|
* is used as a source operand */
|
* is used as a source operand */
|
gen_op_move_t_gpr[k][reg_t[k]](opq, 1);
|
gen_op_move_t_gpr[k][opq->reg_t_d[k]](opq, 0);
|
}
|
}
|
|
|
/* FIXME: Do this in a more elegent way */
|
|
if(!strncmp(or32_opcodes[insn_index].name, "l.jal", 5)) {
|
|
/* In the case of a l.jal instruction, make sure that LINK_REGNO is not in
|
|
* a temporary. The problem is that the l.jal(r) instruction stores the
|
|
* `return address' in LINK_REGNO. The temporaries are shiped out only
|
|
* after the delay slot instruction has executed and so it overwrittes the
|
|
* `return address'. */
|
|
for(k = 0; k < NUM_T_REGS; k++) {
|
|
if(reg_t[k] == LINK_REGNO) {
|
|
gen_op_move_gpr_t[k][LINK_REGNO](opq, 1);
|
|
reg_t[k] = 32;
|
|
opq->reg_t[k] = 32;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Store the state of the temporaries into dyn->ts */
|
|
dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] = 0;
|
|
if(reg_t[0] < 32)
|
|
dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] = reg_t[0];
|
|
if(reg_t[1] < 32)
|
|
dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] |= reg_t[1] << 5;
|
|
if(reg_t[2] < 32)
|
|
dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] |= reg_t[2] << 10;
|
|
|
|
/* To get the execution log correct for instructions like l.lwz r4,0(r4) the
|
/* To get the execution log correct for instructions like l.lwz r4,0(r4) the
|
* effective address needs to be calculated before the instruction is
|
* effective address needs to be calculated before the instruction is
|
* simulated */
|
* simulated */
|
if(do_stats) {
|
if(do_stats) {
|
/* Find any disposition in the instruction */
|
/* Find any disposition in the instruction */
|
Line 1034... |
Line 878... |
}
|
}
|
}
|
}
|
|
|
or32_opcodes[insn_index].exec(opq, param_t, param, delay_insn);
|
or32_opcodes[insn_index].exec(opq, param_t, param, delay_insn);
|
|
|
/* If any sort of analysis is done, store all temporaries and run
|
if(or32_opcodes[insn_index].func_unit != it_exception) {
|
* analysis() */
|
if(do_stats)
|
if(do_stats) {
|
|
ship_gprs_out_t(opq, 1, reg_t);
|
|
for(j = 0; j < NUM_T_REGS; j++)
|
|
reg_t[j] = 32;
|
|
|
|
gen_op_analysis(opq, 1, insn_index, insn);
|
gen_op_analysis(opq, 1, insn_index, insn);
|
}
|
}
|
|
|
/* The call to join_mem_cycles() could be put into the individual operations
|
/* The call to join_mem_cycles() could be put into the individual operations
|
* that emulate the load/store instructions, but then it would be added to
|
* that emulate the load/store instructions, but then it would be added to
|
* the cycle counter before analysis() is called, which not how the complex
|
* the cycle counter before analysis() is called, which is not how the complex
|
* execution modell does it. */
|
* execution model does it. */
|
if((or32_opcodes[insn_index].func_unit == it_load) ||
|
if((or32_opcodes[insn_index].func_unit == it_load) ||
|
(or32_opcodes[insn_index].func_unit == it_store))
|
(or32_opcodes[insn_index].func_unit == it_store))
|
gen_op_join_mem_cycles(opq, 1);
|
gen_op_join_mem_cycles(opq, 1);
|
|
|
/* If a delay sloted instruction is in the delay slot, avoid doing a jump on
|
/* Delay slot instructions get a special scheduler, thus don't generate it
|
* the first delay sloted instruction. The problem with not doing the above
|
* here */
|
* is that the 0x00000000 instruction is a jump instruction, which is used
|
if((or32_opcodes[insn_index].func_unit != it_exception) && !delay_insn)
|
* for padding, and if there ends up being a jump instruction directly after
|
gen_op_do_sched(opq, 1);
|
* some padding and the code jumps to this location (as with the mmu test)
|
|
* the jump instruction will set cpu_state.pc_delay but code will get
|
|
* generated after the jump instruction and before the delay slot
|
|
* instruciton to check cpu_state.pc_delay and jump out if it is set and so
|
|
* we end up jumping out to the padding instruction. With some thought, the
|
|
* 0x00000000 opcode could really have been encoded to some arithmetic
|
|
* operation that would end up nop-ing (or better yet, to the l.nop 0
|
|
* instruction itself) */
|
|
/* If we came up to a page local jump and because it is the delay slot of
|
|
* another delay sloted instruction the case below is skipped and
|
|
* opq->prev->jump_local will remain set to 1, fix this by reseting it now*/
|
|
if(delay_insn && ((or32_opcodes[insn_index].func_unit == it_jump) ||
|
|
(or32_opcodes[insn_index].func_unit == it_branch))) {
|
|
/* We don't generate code to do the relocation so there will be none.
|
|
* Avoid haveing a reference to it */
|
|
/* Also remove the cross reference to it */
|
|
if(opq->prev) {
|
|
if(opq->prev->jump_local == 2) {
|
|
opq->prev->xref->next = cpu_state.inf_xrefs;
|
|
cpu_state.inf_xrefs = opq->prev->xref;
|
|
}
|
|
opq->prev->jump_local = 0;
|
|
}
|
|
delay_insn = 0;
|
|
}
|
|
|
|
/* In the case of an instruction in the delay slot the pc must be updated
|
|
* before op_do_sched runs because if it so happens to generate an exception
|
|
* it will think that we are still executeing the delay slot instruction
|
|
* which infact we have just executed and then SPR_EPCR_BASE will end up
|
|
* pointing to the delay slot instruction, which is wrong. If the delay
|
|
* slot instruction is an exception instruction (l.trap/l.sys) the exception
|
|
* must appear to have been generated in the delay slot */
|
|
if(delay_insn && (or32_opcodes[insn_index].func_unit != it_exception)) {
|
|
if(xref || (delay_insn_type == it_branch))
|
|
gen_op_set_pc_preemt_check(opq, 1);
|
|
else /* delay_insn_tyte == it_jump */
|
|
gen_op_set_pc_preemt(opq, 1);
|
|
/* Move temporaries to their permanent storage */
|
|
ship_gprs_out_t(opq, 1, reg_t);
|
|
}
|
|
|
|
/* Same reason as for the above case */
|
|
if(or32_opcodes[insn_index].func_unit == it_exception) {
|
|
/* FIXME: Do the instruction switch below in a more elegent way */
|
|
if(!strcmp(or32_opcodes[insn_index].name, "l.rfe")) {
|
|
gen_op_set_rfe_pc(opq, 1);
|
|
} else if(!strcmp(or32_opcodes[insn_index].name, "l.sys")) {
|
|
gen_op_set_except_pc(opq, 1, EXCEPT_SYSCALL - 4);
|
|
} else { /* or32_opcodes[insn_index].name == "l.trap" */
|
|
gen_op_set_except_pc(opq, 1, EXCEPT_TRAP - 4);
|
|
}
|
|
gen_op_set_ts_current(opq, 1);
|
|
}
|
}
|
|
|
gen_op_do_sched(opq, 1);
|
/* rec_page is a physical address */
|
|
void recompile_page(struct dyn_page *dyn)
|
|
{
|
|
unsigned int j;
|
|
struct op_queue *opq = cpu_state.opqs;
|
|
oraddr_t rec_addr = dyn->or_page;
|
|
oraddr_t rec_page = dyn->or_page;
|
|
void **loc;
|
|
|
/* If this is an exception instruction then we still need to perform the
|
/* The start of the next page */
|
* exception */
|
rec_page += PAGE_LEN;
|
if(or32_opcodes[insn_index].func_unit == it_exception) {
|
|
/* FIXME: Do the instruction switch below in a more elegent way */
|
|
if(!strcmp(or32_opcodes[insn_index].name, "l.rfe")) {
|
|
gen_op_rfe(opq, 1);
|
|
} else if(!strcmp(or32_opcodes[insn_index].name, "l.sys")) {
|
|
gen_op_do_except(opq, 1, EXCEPT_SYSCALL);
|
|
} else { /* or32_opcodes[insn_index].name == "l.trap" */
|
|
gen_op_do_except(opq, 1, EXCEPT_TRAP);
|
|
}
|
|
}
|
|
|
|
/* FIXME: If the delay slot is cross referenced after we have stuck the jump
|
printf("Recompileing page %"PRIxADDR"\n", rec_addr);
|
* instruction in the operations queue we will genererate temporary->
|
fflush(stdout);
|
* register code after the jump, which will be unreachable. This is no
|
|
* problem as all temporaries are stored in anticipation for a jump. */
|
/* Mark all temporaries as not containing a register */
|
/* FIXME: If the delay slot is cross referenced we should generate the
|
|
* conditional jump code as we do below. This will not happen if the delay
|
|
* slot is cross referenced after we generate the operations for the jump */
|
|
/* FIXME: If the instruction in the delay slot is an exception instruction
|
|
* the code that we generate below will be unreachable since the exception
|
|
* instruction jumps to the exection vector */
|
|
/* Generate code to jump out to the proper location */
|
|
if(delay_insn) {
|
|
for(j = 0; j < NUM_T_REGS; j++)
|
for(j = 0; j < NUM_T_REGS; j++)
|
reg_t[j] = 32;
|
opq->reg_t[j] = 32; /* Out-of-range registers */
|
|
|
if(xref || (delay_insn_type == it_branch)) {
|
dyn->delayr = -verify_memoryarea(rec_addr)->delayr;
|
/* If the delay-slot instruction is cross referenced, then we have to
|
|
* check env->delay_insn */
|
opq->num_ops = 0;
|
if(opq->prev && opq->prev->jump_local) {
|
opq->num_ops_param = 0;
|
gen_op_jmp_imm_check(opq, 1, 0);
|
|
opq->prev->jump_local_loc = &opq->ops_param[opq->num_ops_param - 1];
|
/* Insert code to check if the first instruction is exeucted in a delay slot*/
|
} else {
|
gen_op_check_delay_slot(opq, 1, 0);
|
gen_op_do_jump_check(opq, 1);
|
recompile_insn(opq, rec_addr, 1);
|
}
|
ship_gprs_out_t(opq, 1, opq->reg_t_d);
|
} else if(delay_insn_type == it_jump) {
|
gen_op_do_sched_delay(opq, 1);
|
gen_op_clear_delay_insn(opq, 1);
|
gen_op_clear_delay_insn(opq, 1);
|
if(opq->prev && opq->prev->jump_local) {
|
gen_op_do_jump_delay(opq, 1);
|
/* The 0 will get patched when the page-local jumps get patched */
|
gen_op_mark_loc(opq, 1);
|
gen_op_jmp_imm(opq, 1, 0);
|
|
/* FIXME: opq->ops_param is realloced with realloc and so we risk a
|
|
* reallocation in which the location ends up moveing in memory */
|
|
opq->prev->jump_local_loc = &opq->ops_param[opq->num_ops_param - 1];
|
|
} else {
|
|
gen_op_do_jump(opq, 1);
|
|
}
|
|
}
|
|
delay_insn = 0;
|
|
}
|
|
|
|
/* Set flag for next instruction to be in a delay slot */
|
for(j = 0; j < NUM_T_REGS; j++)
|
if((or32_opcodes[insn_index].func_unit == it_jump) ||
|
opq->reg_t[j] = 32; /* Out-of-range registers */
|
(or32_opcodes[insn_index].func_unit == it_branch)) {
|
|
delay_insn = 1;
|
for(; rec_addr < rec_page; rec_addr += 4, opq = opq->next) {
|
delay_insn_type = or32_opcodes[insn_index].func_unit;
|
if(opq->prev) {
|
}
|
opq->num_ops = 0;
|
|
opq->num_ops_param = 0;
|
}
|
}
|
|
opq->jump_local = -1;
|
|
opq->not_jump_loc = -1;
|
|
|
|
opq->insn_addr = rec_addr;
|
|
|
|
/* Check if this location is cross referenced */
|
|
if(opq->xref) {
|
|
/* If the current address is cross-referenced, the temporaries shall be
|
|
* in an undefined state, so we must assume that no registers reside in
|
|
* them */
|
|
/* Ship out the current set of registers from the temporaries */
|
|
if(opq->prev)
|
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
|
|
if(delay_insn) {
|
for(j = 0; j < NUM_T_REGS; j++)
|
dyn->carrys_delay_slot = 1;
|
opq->reg_t[j] = 32;
|
/* Quick hack to avoid dereferencing an uninitialised pointer below with
|
|
* *opq->jump_local_loc */
|
|
if(opq->prev->jump_local == 2) {
|
|
/* FIXME: In this case the delay slot instruction won't get executed */
|
|
opq->prev->xref->next = cpu_state.inf_xrefs;
|
|
cpu_state.inf_xrefs = opq->prev->xref;
|
|
}
|
}
|
opq->prev->jump_local = 0;
|
|
|
recompile_insn(opq, rec_addr, 0);
|
|
|
|
/* Store the state of the temporaries */
|
|
memcpy(opq->next->reg_t, opq->reg_t_d, sizeof(opq->reg_t));
|
}
|
}
|
|
|
dyn->dirty = 0;
|
dyn->dirty = 0;
|
|
|
|
/* Store the state of the temporaries */
|
|
dyn->ts_bound[PAGE_LEN >> 2] = dyn->ts_during[j];
|
|
|
/* Ship temporaries out to the corrisponding registers */
|
/* Ship temporaries out to the corrisponding registers */
|
ship_gprs_out_t(opq->prev, 1, reg_t);
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
|
|
opq->num_ops = 0;
|
opq->num_ops = 0;
|
opq->num_ops_param = 0;
|
opq->num_ops_param = 0;
|
opq->jump_local = 0;
|
opq->not_jump_loc = -1;
|
|
opq->jump_local = -1;
|
|
|
/* Insert code to jump to the next page */
|
/* Insert code to jump to the next page */
|
gen_op_set_ts_current(opq, 1);
|
gen_op_set_ts_current(opq, 1);
|
gen_op_do_jump_pc(opq, 1);
|
gen_op_do_jump(opq, 1);
|
|
|
/* Generate the code */
|
/* Generate the code */
|
gen_code(cpu_state.opqs, dyn);
|
gen_code(cpu_state.opqs, dyn);
|
|
|
/* Patch the x-ref table */
|
/* Fix up the locations */
|
for(xref = dyn->xrefs; xref; xref = xref->next)
|
for(loc = dyn->locs; loc < &dyn->locs[PAGE_LEN / 4]; loc++)
|
xref->dyn_addr = dyn->host_page + (unsigned int)xref->dyn_addr;
|
*loc += (unsigned int)dyn->host_page;
|
|
|
|
cpu_state.opqs->ops_param[0] += (unsigned int)dyn->host_page;
|
|
|
/* Search for page-local jumps */
|
/* Search for page-local jumps */
|
for(opq = cpu_state.opqs; opq; opq = opq->next) {
|
for(opq = cpu_state.opqs, j = 0; j < (PAGE_LEN / 4); opq = opq->next, j++) {
|
if(opq->jump_local) {
|
if(opq->jump_local != -1)
|
if(opq->jump_local == 2)
|
opq->ops_param[opq->jump_local] =
|
/* This cross reference was not patched above so patch it now */
|
(unsigned int)dyn->locs[opq->jump_local_loc >> 2];
|
opq->xref->dyn_addr = dyn->host_page + (unsigned int)opq->xref->dyn_addr;
|
|
|
if(opq->not_jump_loc != -1)
|
*opq->jump_local_loc = (unsigned int)opq->xref->dyn_addr;
|
opq->ops_param[opq->not_jump_loc] = (unsigned int)dyn->locs[j + 1];
|
if(opq->jump_local == 2) {
|
|
/* Return the xref to the pool of infinite loop cross references */
|
/* Store the state of the temporaries into dyn->ts_bound */
|
opq->xref->next = cpu_state.inf_xrefs;
|
dyn->ts_bound[j] = 0;
|
cpu_state.inf_xrefs = opq->xref;
|
if(opq->reg_t[0] < 32)
|
}
|
dyn->ts_bound[j] = opq->reg_t[0];
|
}
|
if(opq->reg_t[1] < 32)
|
|
dyn->ts_bound[j] |= opq->reg_t[1] << 5;
|
|
if(opq->reg_t[2] < 32)
|
|
dyn->ts_bound[j] |= opq->reg_t[2] << 10;
|
|
|
|
dyn->ts_during[j] = 0;
|
|
if(opq->reg_t_d[0] < 32)
|
|
dyn->ts_during[j] = opq->reg_t_d[0];
|
|
if(opq->reg_t_d[1] < 32)
|
|
dyn->ts_during[j] |= opq->reg_t_d[1] << 5;
|
|
if(opq->reg_t_d[2] < 32)
|
|
dyn->ts_during[j] |= opq->reg_t_d[2] << 10;
|
}
|
}
|
|
|
/* Patch the relocations */
|
/* Patch the relocations */
|
patch_relocs(cpu_state.opqs, dyn->host_page);
|
patch_relocs(cpu_state.opqs, dyn->host_page);
|
|
|
/* FIXME: Fix the issue below in a more elegent way */
|
/* FIXME: Fix the issue below in a more elegent way */
|
/* Since eval_insn is called to get the instruction, runtime.sim.mem_cycles is
|
/* Since eval_insn is called to get the instruction, runtime.sim.mem_cycles is
|
* updated but the recompiler expectes it to start a 0, so reset it */
|
* updated but the recompiler expectes it to start a 0, so reset it */
|
runtime.sim.mem_cycles = 0;
|
runtime.sim.mem_cycles = 0;
|
|
|
#if 0
|
|
This is very usefull during debuging
|
|
/* Count the number of infinite loop cross references (to make sure that we
|
|
* returned them all) */
|
|
for(j = 0, xref = cpu_state.inf_xrefs; xref; xref = xref->next) {
|
|
printf("Cross reference to %"PRIxADDR" is here\n", xref->or_addr);
|
|
j++;
|
|
}
|
|
|
|
if(j != (PAGE_LEN / 4)) {
|
|
fprintf(stderr, "Infinite loop cross references are leaked!\n");
|
|
fprintf(stderr, "Number in free list now: %i, meant to be: %i\n", j, PAGE_LEN / 4);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
struct x_ref *add_to_xrefs(struct dyn_page *dp, oraddr_t addr)
|
|
{
|
|
struct x_ref *new;
|
|
struct x_ref *cur;
|
|
struct x_ref *prev;
|
|
|
|
new = malloc(sizeof(struct x_ref));
|
|
|
|
new->ref = 1;
|
|
new->or_addr = addr;
|
|
|
|
/* Find the location to insert the address */
|
|
for(cur = dp->xrefs, prev = NULL; cur; prev = cur, cur = cur->next) {
|
|
if(cur->or_addr > addr)
|
|
break;
|
|
}
|
|
|
|
if(prev)
|
|
prev->next = new;
|
|
else
|
|
dp->xrefs = new;
|
|
new->next = cur;
|
|
|
|
return new;
|
|
}
|
}
|
|
|
/* Returns non-zero if the jump is into this page, 0 otherwise */
|
/* Returns non-zero if the jump is into this page, 0 otherwise */
|
static int find_jump_loc(oraddr_t j_ea, struct op_queue *opq)
|
static int find_jump_loc(oraddr_t j_ea, struct op_queue *opq)
|
{
|
{
|
struct dyn_page *dp;
|
|
int i;
|
int i;
|
struct x_ref *xref = NULL;
|
|
int *ops;
|
|
|
|
/* Mark the jump as non page local if the delay slot instruction is on the
|
/* Mark the jump as non page local if the delay slot instruction is on the
|
* next page to the jump instruction. This should not be needed */
|
* next page to the jump instruction. This should not be needed */
|
if((ADDR_PAGE(j_ea) != ADDR_PAGE(opq->insn_addr)) ||
|
if((ADDR_PAGE(j_ea) != ADDR_PAGE(opq->insn_addr)) ||
|
(ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)))
|
(ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)))
|
Line 1292... |
Line 1040... |
* page */
|
* page */
|
return 0;
|
return 0;
|
|
|
/* The jump is into the page currently undergoing dynamic recompilation */
|
/* The jump is into the page currently undergoing dynamic recompilation */
|
|
|
/* FIXME: It would be great if we didn't have to do this (find_dynd...) (it is
|
|
* already passed to recompile_page) */
|
|
dp = find_dynd_page(j_ea);
|
|
|
|
/* Check if we have already x-refed this location */
|
|
if((xref = find_host_x_ref(dp->xrefs, j_ea))) {
|
|
/* If we have already x-refed this location, don't x-ref it again */
|
|
if(!find_held_x_ref(dp->held_xrefs, j_ea)) {
|
|
xref->ref++;
|
|
add_to_held_xrefs(dp, xref);
|
|
}
|
|
} else {
|
|
/* Stick this address into the page's x-ref table */
|
|
xref = add_to_xrefs(dp, j_ea);
|
|
add_to_held_xrefs(dp, xref);
|
|
}
|
|
|
|
opq->xref = xref;
|
|
|
|
/* If we haven't got to the location of the jump, everything is ok */
|
/* If we haven't got to the location of the jump, everything is ok */
|
if(j_ea > opq->insn_addr)
|
if(j_ea > opq->insn_addr) {
|
|
/* Find the corissponding opq and mark it as cross referenced */
|
|
for(i = (j_ea - opq->insn_addr) / 4; i; i--)
|
|
opq = opq->next;
|
|
opq->xref = 1;
|
return 1;
|
return 1;
|
|
}
|
|
|
/* Insert temporary -> register code before the jump ea and register ->
|
/* Insert temporary -> register code before the jump ea and register ->
|
* temporary at the x-ref address */
|
* temporary at the x-ref address */
|
while(opq->insn_addr > j_ea) opq = opq->prev;
|
for(i = (opq->insn_addr - j_ea) / 4; i; i--)
|
|
opq = opq->prev;
|
|
|
if(!opq->prev)
|
if(!opq->prev)
|
/* We're at the begining of a page, no need to do anything */
|
/* We're at the begining of a page, no need to do anything */
|
return 1;
|
return 1;
|
|
|
/* Found location, insert code */
|
/* Found location, insert code */
|
|
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
|
|
for(i = 0; i < NUM_T_REGS; i++) {
|
for(i = 0; i < NUM_T_REGS; i++) {
|
if(opq->reg_t[i] < 32) {
|
if(opq->reg_t[i] < 32) {
|
gen_op_move_t_gpr[i][opq->reg_t[i]](opq, 0);
|
gen_op_move_t_gpr[i][opq->reg_t[i]](opq, 0);
|
opq->reg_t[i] = 32;
|
opq->reg_t[i] = 32;
|
}
|
}
|
}
|
}
|
|
|
/* In the event of a page local jump that jumps backwards (l.j -4) the cross
|
opq->xref = 1;
|
* reference to the target may not have existed when the jump-ed to adress was
|
|
* recompiled and if the jump-ed to address is in the delay slot of another
|
|
* jump instruction an op_jmp_imm_check operation must be generated and not an
|
|
* op_jmp_imm operation */
|
|
for(ops = opq->ops, i = 0; i < opq->num_ops; i++, ops++) {
|
|
if(*ops == op_jmp_imm_indx)
|
|
*ops = op_jmp_imm_check_indx;
|
|
else if(*ops == op_set_pc_preemt_indx)
|
|
*ops = op_set_pc_preemt_check_indx;
|
|
}
|
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
static void gen_j_imm(struct op_queue *opq, oraddr_t off)
|
|
{
|
|
int jump_local;
|
|
int i;
|
|
int reg_t[NUM_T_REGS];
|
|
|
|
off <<= 2;
|
|
|
|
jump_local = find_jump_loc(opq->insn_addr + off, opq);
|
|
|
|
if(ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)) {
|
|
gen_op_set_pc_delay_imm(opq, 1, off);
|
|
gen_op_do_sched(opq, 1);
|
|
return;
|
|
}
|
|
|
|
gen_op_set_delay_insn(opq, 1);
|
|
gen_op_do_sched(opq, 1);
|
|
|
|
/* Recompileing the delay slot instruction must see the temoraries being in
|
|
* the state after the jump/branch instruction not before */
|
|
memcpy(reg_t, opq->reg_t, sizeof(reg_t));
|
|
memcpy(opq->reg_t, opq->reg_t_d, sizeof(reg_t));
|
|
|
|
/* Generate the delay slot instruction */
|
|
recompile_insn(opq, opq->insn_addr + 4, 1);
|
|
|
|
memcpy(opq->reg_t, reg_t, sizeof(reg_t));
|
|
|
|
ship_gprs_out_t(opq, 1, opq->reg_t_d);
|
|
|
|
gen_op_add_pc(opq, 1, (orreg_t)off - 8);
|
|
gen_op_clear_delay_insn(opq, 1);
|
|
gen_op_do_sched_delay(opq, 1);
|
|
|
|
if(jump_local) {
|
|
gen_op_jmp_imm(opq, 1, 0);
|
|
opq->jump_local = opq->num_ops_param - 1;
|
|
opq->jump_local_loc = (opq->insn_addr + (orreg_t)off) & (PAGE_LEN - 1);
|
|
} else
|
|
gen_op_do_jump(opq, 1);
|
|
}
|
|
|
|
static const generic_gen_op set_pc_delay_gpr[32] = {
|
|
NULL,
|
|
gen_op_move_gpr1_pc_delay,
|
|
gen_op_move_gpr2_pc_delay,
|
|
gen_op_move_gpr3_pc_delay,
|
|
gen_op_move_gpr4_pc_delay,
|
|
gen_op_move_gpr5_pc_delay,
|
|
gen_op_move_gpr6_pc_delay,
|
|
gen_op_move_gpr7_pc_delay,
|
|
gen_op_move_gpr8_pc_delay,
|
|
gen_op_move_gpr9_pc_delay,
|
|
gen_op_move_gpr10_pc_delay,
|
|
gen_op_move_gpr11_pc_delay,
|
|
gen_op_move_gpr12_pc_delay,
|
|
gen_op_move_gpr13_pc_delay,
|
|
gen_op_move_gpr14_pc_delay,
|
|
gen_op_move_gpr15_pc_delay,
|
|
gen_op_move_gpr16_pc_delay,
|
|
gen_op_move_gpr17_pc_delay,
|
|
gen_op_move_gpr18_pc_delay,
|
|
gen_op_move_gpr19_pc_delay,
|
|
gen_op_move_gpr20_pc_delay,
|
|
gen_op_move_gpr21_pc_delay,
|
|
gen_op_move_gpr22_pc_delay,
|
|
gen_op_move_gpr23_pc_delay,
|
|
gen_op_move_gpr24_pc_delay,
|
|
gen_op_move_gpr25_pc_delay,
|
|
gen_op_move_gpr26_pc_delay,
|
|
gen_op_move_gpr27_pc_delay,
|
|
gen_op_move_gpr28_pc_delay,
|
|
gen_op_move_gpr29_pc_delay,
|
|
gen_op_move_gpr30_pc_delay,
|
|
gen_op_move_gpr31_pc_delay };
|
|
|
|
static void gen_j_reg(struct op_queue *opq, unsigned int gpr, int insn_index,
|
|
uint32_t insn)
|
|
{
|
|
int i;
|
|
int reg_t[NUM_T_REGS];
|
|
|
|
if(do_stats)
|
|
gen_op_analysis(opq, 1, insn_index, insn);
|
|
|
|
if(!gpr)
|
|
gen_op_clear_pc_delay(opq, 1);
|
|
else
|
|
set_pc_delay_gpr[gpr](opq, 1);
|
|
|
|
gen_op_do_sched(opq, 1);
|
|
|
|
/* Recompileing the delay slot instruction must see the temoraries being in
|
|
* the state after the jump/branch instruction not before */
|
|
memcpy(reg_t, opq->reg_t, sizeof(reg_t));
|
|
memcpy(opq->reg_t, opq->reg_t_d, sizeof(reg_t));
|
|
|
|
/* Generate the delay slot instruction */
|
|
gen_op_set_delay_insn(opq, 1);
|
|
recompile_insn(opq, opq->insn_addr + 4, 1);
|
|
|
|
memcpy(opq->reg_t, reg_t, sizeof(reg_t));
|
|
|
|
ship_gprs_out_t(opq, 1, opq->reg_t_d);
|
|
|
|
gen_op_set_pc_pc_delay(opq, 1);
|
|
gen_op_clear_delay_insn(opq, 1);
|
|
gen_op_do_sched_delay(opq, 1);
|
|
|
|
gen_op_do_jump_delay(opq, 1);
|
|
}
|
|
|
/*------------------------------[ Operation generation for an instruction ]---*/
|
/*------------------------------[ Operation generation for an instruction ]---*/
|
/* FIXME: Flag setting is not done in any instruction */
|
/* FIXME: Flag setting is not done in any instruction */
|
/* FIXME: Since r0 is not moved into a temporary, check all arguments below! */
|
/* FIXME: Since r0 is not moved into a temporary, check all arguments below! */
|
|
|
static const generic_gen_op clear_t[NUM_T_REGS] =
|
static const generic_gen_op clear_t[NUM_T_REGS] =
|
Line 1491... |
Line 1331... |
}
|
}
|
|
|
void gen_l_bf(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_bf(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
|
int i;
|
gen_op_check_flag(opq, 1, param[0] << 2);
|
if(do_stats)
|
|
gen_op_analysis(opq, 1, 3, 0x10000000 | (param[0] & 0x03ffffff));
|
|
|
|
/* The temporaries are expected to be shiped out after the execution of the
|
|
* branch instruction wether it branched or not */
|
|
if(opq->prev) {
|
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
|
for(i = 0; i < NUM_T_REGS; i++) {
|
|
opq->reg_t[i] = 32;
|
|
opq->reg_t_d[i] = 32;
|
|
}
|
|
}
|
|
|
|
if(ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)) {
|
|
gen_op_check_flag_delay(opq, 1, param[0] << 2);
|
|
gen_op_do_sched(opq, 1);
|
|
opq->not_jump_loc = -1;
|
|
return;
|
|
}
|
|
|
|
gen_op_check_flag(opq, 1, 0);
|
|
opq->not_jump_loc = opq->num_ops_param - 1;
|
|
|
|
gen_j_imm(opq, param[0]);
|
}
|
}
|
|
|
void gen_l_bnf(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_bnf(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
|
int i;
|
gen_op_check_not_flag(opq, 1, param[0] << 2);
|
if(do_stats)
|
|
gen_op_analysis(opq, 1, 2, 0x0c000000 | (param[0] & 0x03ffffff));
|
|
|
|
/* The temporaries are expected to be shiped out after the execution of the
|
|
* branch instruction wether it branched or not */
|
|
if(opq->prev) {
|
|
ship_gprs_out_t(opq->prev, 1, opq->reg_t);
|
|
for(i = 0; i < NUM_T_REGS; i++) {
|
|
opq->reg_t[i] = 32;
|
|
opq->reg_t_d[i] = 32;
|
|
}
|
|
}
|
|
|
|
if(ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)) {
|
|
gen_op_check_not_flag_delay(opq, 1, param[0] << 2);
|
|
gen_op_do_sched(opq, 1);
|
|
opq->not_jump_loc = -1;
|
|
return;
|
|
}
|
|
|
|
gen_op_check_not_flag(opq, 1, 0);
|
|
opq->not_jump_loc = opq->num_ops_param - 1;
|
|
|
|
gen_j_imm(opq, param[0]);
|
|
|
|
/* The temporaries don't get shiped out if the branch is not taken */
|
|
memcpy(opq->next->reg_t, opq->reg_t, sizeof(opq->reg_t));
|
}
|
}
|
|
|
static const generic_gen_op l_cmov_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
|
static const generic_gen_op l_cmov_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
|
/* param0 -> t0 */ {
|
/* param0 -> t0 */ {
|
/* param0 -> t0, param1 -> t0 */ { NULL, gen_op_cmov_t0_t0_t1, gen_op_cmov_t0_t0_t2 },
|
/* param0 -> t0, param1 -> t0 */ { NULL, gen_op_cmov_t0_t0_t1, gen_op_cmov_t0_t0_t2 },
|
Line 1607... |
Line 1496... |
/* param0 -> t2, param1 -> t2 */ { gen_op_div_t2_t2_t0, gen_op_div_t2_t2_t1, gen_op_div_t2_t2_t2 } } };
|
/* param0 -> t2, param1 -> t2 */ { gen_op_div_t2_t2_t0, gen_op_div_t2_t2_t1, gen_op_div_t2_t2_t2 } } };
|
|
|
void gen_l_div(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_div(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* Cross reference this location, since an ILLEGAL exception may happen */
|
|
find_jump_loc(opq->insn_addr, opq);
|
|
if(!param[2]) {
|
if(!param[2]) {
|
/* There is no option. This _will_ cause an illeagal exception */
|
/* There is no option. This _will_ cause an illeagal exception */
|
if(!delay_slot)
|
if(!delay_slot)
|
gen_op_illegal(opq, 1);
|
gen_op_illegal(opq, 1);
|
else
|
else
|
Line 1652... |
Line 1539... |
/* param0 -> t2, param1 -> t2 */ { gen_op_divu_t2_t2_t0, gen_op_divu_t2_t2_t1, gen_op_divu_t2_t2_t2 } } };
|
/* param0 -> t2, param1 -> t2 */ { gen_op_divu_t2_t2_t0, gen_op_divu_t2_t2_t1, gen_op_divu_t2_t2_t2 } } };
|
|
|
void gen_l_divu(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_divu(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* Cross reference this location, since an ILLEGAL exception may happen */
|
|
find_jump_loc(opq->insn_addr, opq);
|
|
if(!param[2]) {
|
if(!param[2]) {
|
/* There is no option. This _will_ cause an illeagal exception */
|
/* There is no option. This _will_ cause an illeagal exception */
|
if(!delay_slot)
|
if(!delay_slot)
|
gen_op_illegal(opq, 1);
|
gen_op_illegal(opq, 1);
|
else
|
else
|
Line 1814... |
Line 1699... |
}
|
}
|
|
|
void gen_l_j(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_j(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
gen_op_set_pc_delay_imm(opq, 1, param[0] << 2);
|
if(do_stats)
|
|
gen_op_analysis(opq, 1, 0, param[0] & 0x03ffffff);
|
|
|
/* Don't allocate a seporate x-ref structure for the infinite loop instruction
|
gen_j_imm(opq, param[0]);
|
* (l.j 0) */
|
|
if(!param[0]) {
|
|
opq->jump_local = 2;
|
|
opq->xref = cpu_state.inf_xrefs;
|
|
opq->xref->or_addr = opq->insn_addr;
|
|
cpu_state.inf_xrefs = opq->xref->next;
|
|
return;
|
|
}
|
|
|
|
opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
|
|
}
|
}
|
|
|
void gen_l_jal(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_jal(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* It is highly likely that the location that was jumped to will `return'.
|
|
* Therefore, insert a cross reference at that address */
|
|
find_jump_loc(opq->insn_addr + 8, opq);
|
|
|
|
gen_l_j(opq, param_t, param, delay_slot);
|
|
|
|
/* Store the return address */
|
/* Store the return address */
|
gen_op_store_link_addr_gpr(opq, 1);
|
gen_op_store_link_addr_gpr(opq, 1);
|
}
|
|
|
|
static const generic_gen_op set_pc_delay_t[NUM_T_REGS] =
|
if(do_stats)
|
{ gen_op_set_pc_delay_t0, gen_op_set_pc_delay_t1, gen_op_set_pc_delay_t2 };
|
gen_op_analysis(opq, 1, 1, 0x04000000 | (param[0] & 0x03ffffff));
|
|
|
|
gen_j_imm(opq, param[0]);
|
|
}
|
|
|
void gen_l_jr(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_jr(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* Treat all jumps as non page-local */
|
gen_j_reg(opq, param[0], 104, 0x14000000 | (param[0] << 11));
|
opq->jump_local = 0;
|
|
|
|
if(!param[0]) {
|
|
gen_op_clear_pc_delay(opq, 1);
|
|
return;
|
|
}
|
|
|
|
set_pc_delay_t[param_t[0]](opq, 1);
|
|
}
|
}
|
|
|
void gen_l_jalr(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_jalr(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* It is highly likely that the location that was jumped to will `return'.
|
|
* Therefore, insert a cross reference at that address */
|
|
find_jump_loc(opq->insn_addr + 8, opq);
|
|
|
|
gen_l_jr(opq, param_t, param, delay_slot);
|
|
|
|
/* Store the return address */
|
/* Store the return address */
|
gen_op_store_link_addr_gpr(opq, 1);
|
gen_op_store_link_addr_gpr(opq, 1);
|
|
|
|
gen_j_reg(opq, param[0], 105, 0x18000000 | (param[0] << 11));
|
}
|
}
|
|
|
/* FIXME: Optimise all load instruction when the disposition == 0 */
|
/* FIXME: Optimise all load instruction when the disposition == 0 */
|
|
|
static const imm_gen_op l_lbs_imm_t_table[NUM_T_REGS] =
|
static const imm_gen_op l_lbs_imm_t_table[NUM_T_REGS] =
|
Line 2293... |
Line 2153... |
}
|
}
|
|
|
void gen_l_rfe(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_rfe(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
|
if(do_stats)
|
|
gen_op_analysis(opq, 1, 12, 0x24000000);
|
|
|
gen_op_prep_rfe(opq, 1);
|
gen_op_prep_rfe(opq, 1);
|
|
gen_op_do_sched(opq, 1);
|
|
gen_op_do_jump(opq, 1);
|
}
|
}
|
|
|
/* FIXME: All store instructions should be optimised when the disposition = 0 */
|
/* FIXME: All store instructions should be optimised when the disposition = 0 */
|
|
|
static const imm_gen_op l_sb_clear_table[NUM_T_REGS] =
|
static const imm_gen_op l_sb_clear_table[NUM_T_REGS] =
|
Line 2989... |
Line 2854... |
|
|
/* FIXME: This will not work if the l.sys is in a delay slot */
|
/* FIXME: This will not work if the l.sys is in a delay slot */
|
void gen_l_sys(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_sys(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* Since we *know* that we *will* jump to the next instruction, insert an xref
|
if(do_stats)
|
* there */
|
gen_op_analysis(opq, 1, 7, 0x20000000 | param[0]);
|
find_jump_loc(opq->insn_addr + 4, opq);
|
|
|
|
if(!delay_slot)
|
if(!delay_slot)
|
gen_op_prep_sys(opq, 1);
|
gen_op_prep_sys(opq, 1);
|
else
|
else
|
gen_op_prep_sys_delay(opq, 1);
|
gen_op_prep_sys_delay(opq, 1);
|
|
|
|
gen_op_do_sched(opq, 1);
|
|
gen_op_do_jump(opq, 1);
|
}
|
}
|
|
|
/* FIXME: This will not work if the l.trap is in a delay slot */
|
/* FIXME: This will not work if the l.trap is in a delay slot */
|
void gen_l_trap(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_trap(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* Since we *know* that we *will* jump to the next instruction, insert an xref
|
if(do_stats)
|
* there */
|
gen_op_analysis(opq, 1, 8, 0x22000000);
|
find_jump_loc(opq->insn_addr + 4, opq);
|
|
|
|
if(!delay_slot)
|
if(!delay_slot)
|
gen_op_prep_trap(opq, 1);
|
gen_op_prep_trap(opq, 1);
|
else
|
else
|
gen_op_prep_trap_delay(opq, 1);
|
gen_op_prep_trap_delay(opq, 1);
|
Line 3068... |
Line 2934... |
}
|
}
|
|
|
void gen_l_invalid(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
void gen_l_invalid(struct op_queue *opq, int param_t[3], orreg_t param[3],
|
int delay_slot)
|
int delay_slot)
|
{
|
{
|
/* The program running on openrisc may decide to patch this location, so
|
|
* just cross reference this location just-in-case */
|
|
find_jump_loc(opq->insn_addr, opq);
|
|
if(!delay_slot)
|
if(!delay_slot)
|
gen_op_illegal(opq, 1);
|
gen_op_illegal(opq, 1);
|
else
|
else
|
gen_op_illegal_delay(opq, 1);
|
gen_op_illegal_delay(opq, 1);
|
}
|
}
|