URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
[/] [openrisc/] [trunk/] [or1ksim/] [cpu/] [or32/] [dyn-rec.c] - Rev 294
Go to most recent revision | Compare with Previous | Blame | View Log
/* dyn-rec.c -- Dynamic recompiler implementation for or32 Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org This file is part of OpenRISC 1000 Architectural Simulator. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <signal.h> #include <errno.h> #include <execinfo.h> #include "config.h" #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #include "port.h" #include "arch.h" #include "immu.h" #include "abstract.h" #include "opcode/or32.h" #include "spr-defs.h" #include "execute.h" #include "except.h" #include "spr-defs.h" #include "sim-config.h" #include "sched.h" #include "i386-regs.h" #include "def-op-t.h" #include "dyn-rec.h" #include "gen-ops.h" #include "op-support.h" #include "toplevel-support.h" /* NOTE: All openrisc (or) addresses in this file are *PHYSICAL* addresses */ /* FIXME: Optimise sorted list adding */ typedef void (*generic_gen_op)(struct op_queue *opq, int end); typedef void (*imm_gen_op)(struct op_queue *opq, int end, uorreg_t imm); void gen_l_invalid(struct op_queue *opq, int param_t[3], int delay_slot); /* ttg->temporary to gpr */ DEF_GPR_OP(generic_gen_op, gen_op_move_gpr_t, gen_op_ttg_gpr); /* gtt->gpr to temporary */ DEF_GPR_OP(generic_gen_op, gen_op_move_t_gpr, gen_op_gtt_gpr); DEF_1T_OP(imm_gen_op, calc_insn_ea_table, gen_op_calc_insn_ea); /* Linker stubs. This will allow the linker to link in op.o. The relocations * that the linker does for these will be irrelevent anyway, since we patch the * relocations during recompilation. */ uorreg_t __op_param1; uorreg_t __op_param2; uorreg_t __op_param3; /* The number of bytes that a dynamicly recompiled page should be enlarged by */ #define RECED_PAGE_ENLARGE_BY 51200 /* The number of entries that the micro operations array in op_queue should be * enlarged by */ #define OPS_ENLARGE_BY 5 #define T_NONE (-1) /* Temporary is used as a source operand */ #define TFLAG_SRC 1 /* Temporary is used as a destination operand */ #define TFLAG_DST 2 /* Temporary has been saved to permanent storage */ #define TFLAG_SAVED 4 /* Temporary contains the value of the register before the instruction execution * occurs (either by an explicit reg->t move or implicitly being left over from * a previous instruction) */ #define TFLAG_SOURCED 8 /* FIXME: Put this into some header */ extern int do_stats; static int sigsegv_state = 0; static void *sigsegv_addr = NULL; void dyn_ret_stack_prot(void); void dyn_sigsegv_debug(int u, siginfo_t *siginf, void *dat) { struct dyn_page *dp; FILE *f; char filen[18]; /* 18 == strlen("or_page.%08x") + 1 */ int i; struct sigcontext *sigc = dat; if(!sigsegv_state) { sigsegv_addr = siginf->si_addr; } else { fprintf(stderr, "Nested SIGSEGV occured, dumping next chuck of info\n"); sigsegv_state++; } /* First dump all the data that does not need dereferenceing to get */ switch(sigsegv_state) { case 0: fflush(stderr); fprintf(stderr, "Segmentation fault on acces to %p at 0x%08lx, (or address: 0x%"PRIxADDR")\n\n", sigsegv_addr, sigc->eip, cpu_state.pc); sigsegv_state++; case 1: /* Run through the recompiled pages, dumping them to disk as we go */ for(i = 0; i < (2 << (32 - immu_state->pagesize_log2)); i++) { dp = cpu_state.dyn_pages[i]; if(!dp) continue; fprintf(stderr, "Dumping%s page 0x%"PRIxADDR" recompiled to %p (len: %u) to disk\n", dp->dirty ? " dirty" : "", dp->or_page, dp->host_page, dp->host_len); fflush(stdout); sprintf(filen, "or_page.%"PRIxADDR, dp->or_page); if(!(f = fopen(filen, "w"))) { fprintf(stderr, "Unable to open %s to dump the recompiled page to: %s\n", filen, strerror(errno)); continue; } if(fwrite(dp->host_page, dp->host_len, 1, f) < 1) fprintf(stderr, "Unable to write recompiled data to file: %s\n", strerror(errno)); fclose(f); } sigsegv_state++; case 2: sim_done(); } } struct dyn_page *new_dp(oraddr_t page) { struct dyn_page *dp = malloc(sizeof(struct dyn_page)); dp->or_page = IADDR_PAGE(page); dp->locs = malloc(sizeof(void *) * (immu_state->pagesize / 4)); dp->host_len = 0; dp->host_page = NULL; dp->dirty = 1; if(do_stats) { dp->insns = malloc(immu_state->pagesize); dp->insn_indexs = malloc(sizeof(unsigned int) * (immu_state->pagesize / 4)); } cpu_state.dyn_pages[dp->or_page >> immu_state->pagesize_log2] = dp; return dp; } void dyn_main(void) { struct dyn_page *target_dp; oraddr_t phys_page; setjmp(cpu_state.excpt_loc); for(;;) { phys_page = immu_translate(cpu_state.pc); /* printf("Recompiled code jumping out to %"PRIxADDR" from %"PRIxADDR"\n", phys_page, cpu_state.sprs[SPR_PPC] - 4); */ /* immu_translate() adds the hit delay to runtime.sim.mem_cycles but we add * it to the cycles when the instruction is executed so if we don't reset it * now it will produce wrong results */ runtime.sim.mem_cycles = 0; target_dp = cpu_state.dyn_pages[phys_page >> immu_state->pagesize_log2]; if(!target_dp) target_dp = new_dp(phys_page); /* Since writes to the 0x0-0xff range do not dirtyfy a page recompile the * 0x0 page if the jump is to that location */ if(phys_page < 0x100) target_dp->dirty = 1; if(target_dp->dirty) recompile_page(target_dp); cpu_state.curr_page = target_dp; /* FIXME: If the page is backed by more than one type of memory, this will * produce wrong results */ cpu_state.cycles_dec = target_dp->delayr; if(cpu_state.sprs[SPR_SR] & SPR_SR_IME) /* Add the mmu hit delay to the cycle counter */ cpu_state.cycles_dec -= immu_state->hitdelay; /* FIXME: ebp, ebx, esi and edi are expected to be preserved across function * calls but the recompiled code trashes them... */ enter_dyn_code(phys_page, target_dp); } } static void immu_retranslate(void *dat) { int got_en_dis = (int)dat; immu_translate(cpu_state.pc); runtime.sim.mem_cycles = 0; /* Only update the cycle decrementer if the mmu got enabled or disabled */ if(got_en_dis == IMMU_GOT_ENABLED) /* Add the mmu hit delay to the cycle counter */ cpu_state.cycles_dec = cpu_state.curr_page->delayr - immu_state->hitdelay; else if(got_en_dis == IMMU_GOT_DISABLED) cpu_state.cycles_dec = cpu_state.curr_page->delayr; } /* 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 * hit delay counter */ void recheck_immu(int got_en_dis) { oraddr_t addr; if(cpu_state.delay_insn) addr = cpu_state.pc_delay; else addr = cpu_state.pc + 4; if(IADDR_PAGE(cpu_state.pc) == IADDR_PAGE(addr)) /* Schedule a job to do immu_translate() */ SCHED_ADD(immu_retranslate, (void *)got_en_dis, 0); } /* Runs the scheduler. Called from except_handler (and dirtyfy_page below) */ void run_sched_out_of_line(void) { oraddr_t off = (cpu_state.pc & immu_state->page_offset_mask) >> 2; if(do_stats) { cpu_state.iqueue.insn_addr = cpu_state.pc; cpu_state.iqueue.insn = cpu_state.curr_page->insns[off]; cpu_state.iqueue.insn_index = cpu_state.curr_page->insn_indexs[off]; runtime.cpu.instructions++; analysis(&cpu_state.iqueue); } /* Run the scheduler */ scheduler.job_queue->time += cpu_state.cycles_dec; runtime.sim.cycles -= cpu_state.cycles_dec; op_join_mem_cycles(); if(scheduler.job_queue->time <= 0) do_scheduler(); } /* Signals a page as dirty */ static void dirtyfy_page(struct dyn_page *dp) { oraddr_t check; printf("Dirtyfying page 0x%"PRIxADDR"\n", dp->or_page); dp->dirty = 1; /* If the execution is currently in the page that was touched then recompile * it now and jump back to the point of execution */ check = cpu_state.delay_insn ? cpu_state.pc_delay : cpu_state.pc + 4; if(IADDR_PAGE(check) == dp->or_page) { run_sched_out_of_line(); recompile_page(dp); cpu_state.delay_insn = 0; /* Jump out to the next instruction */ do_jump(check); } } /* Checks to see if a write happened to a recompiled page. If so marks it as * dirty */ void dyn_checkwrite(oraddr_t addr) { /* FIXME: Do this with mprotect() */ struct dyn_page *dp = cpu_state.dyn_pages[addr >> immu_state->pagesize_log2]; /* Since the locations 0x0-0xff are nearly always written to in an exception * handler, ignore any writes to these locations. If code ends up jumping * out there, we'll recompile when the jump actually happens. */ if((addr > 0x100) && dp && !dp->dirty) dirtyfy_page(dp); } /* Moves the temprary t to its permanent storage if it has been used as a * destination register */ static void ship_t_out(struct op_queue *opq, unsigned int t) { unsigned int gpr = opq->reg_t[t]; for(; opq; opq = opq->prev) { if(opq->reg_t[t] != gpr) return; if((opq->tflags[t] & TFLAG_DST) && !(opq->tflags[t] & TFLAG_SAVED)) { opq->tflags[t] |= TFLAG_SAVED; /* FIXME: Check if this is still neccesary */ /* 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(opq->num_ops && (opq->ops[opq->num_ops - 1] == op_do_sched_indx)) { opq->num_ops--; gen_op_move_gpr_t[t][gpr](opq, 1); gen_op_do_sched(opq, 1); return; } gen_op_move_gpr_t[t][gpr](opq, 1); return; } } } static void ship_gprs_out_t(struct op_queue *opq) { int i; if(!opq) return; for(i = 0; i < NUM_T_REGS; i++) { if(opq->reg_t[i] < 32) /* Ship temporaries out in the last opq that actually touched it */ ship_t_out(opq, i); } } /* FIXME: Look at the following instructions to make a better guess at which * temporary to return */ static int find_t(struct op_queue *opq, unsigned int reg) { int i, j, t = -1; for(i = 0; i < NUM_T_REGS; i++) { if(opq->reg_t[i] == reg) return i; /* Ok, we have found an as-yet unused temporary, check if it is needed * later in this instruction */ for(j = 0; j < opq->param_num; j++) { if((opq->param_type[j] & OPTYPE_REG) && (opq->param[j] == opq->reg_t[i])) break; } if(j != opq->param_num) continue; /* We have found the temporary (temporarily:) fit for use */ if((t == -1) || (opq->reg_t[i] == 32)) t = i; } return t; } /* Checks if there is enough space in dp->host_page, if not grow it */ void *enough_host_page(struct dyn_page *dp, void *cur, unsigned int *len, unsigned int amount) { unsigned int used = cur - dp->host_page; /* The array is long enough */ if((used + amount) <= *len) return cur; /* Reallocate */ *len += RECED_PAGE_ENLARGE_BY; if(!(dp->host_page = realloc(dp->host_page, *len))) { fprintf(stderr, "OOM\n"); exit(1); } return dp->host_page + used; } /* Adds an operation to the opq */ void add_to_opq(struct op_queue *opq, int end, int op) { if(opq->num_ops == opq->ops_len) { opq->ops_len += OPS_ENLARGE_BY; if(!(opq->ops = realloc(opq->ops, opq->ops_len * sizeof(int)))) { fprintf(stderr, "OOM\n"); exit(1); } } if(end) opq->ops[opq->num_ops] = op; else { /* Shift everything over by one */ memmove(opq->ops + 1, opq->ops, opq->num_ops* sizeof(int)); opq->ops[0] = op; } 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 */ void add_to_op_params(struct op_queue *opq, int end, unsigned long param) { if(opq->num_ops_param == opq->ops_param_len) { opq->ops_param_len += OPS_ENLARGE_BY; if(!(opq->ops_param = realloc(opq->ops_param, opq->ops_param_len * sizeof(int)))) { fprintf(stderr, "OOM\n"); exit(1); } } if(end) opq->ops_param[opq->num_ops_param] = param; else { /* Shift everything over by one */ memmove(opq->ops_param + 1, opq->ops_param, opq->num_ops_param); opq->ops_param[0] = param; } opq->num_ops_param++; } /* Initialises the recompiler */ void init_dyn_recomp(void) { struct sigaction sigact; struct op_queue *opq = NULL; unsigned int i; cpu_state.opqs = NULL; /* Allocate the operation queue list (+1 for the page chaining) */ for(i = 0; i < (immu_state->pagesize / 4) + 1; i++) { if(!(opq = malloc(sizeof(struct op_queue)))) { fprintf(stderr, "OOM\n"); exit(1); } /* initialise some fields */ opq->ops_len = 0; opq->ops = NULL; opq->ops_param_len = 0; opq->ops_param = NULL; opq->xref = 0; if(cpu_state.opqs) cpu_state.opqs->prev = opq; opq->next = cpu_state.opqs; cpu_state.opqs = opq; } opq->prev = NULL; cpu_state.curr_page = NULL; if(!(cpu_state.dyn_pages = malloc(sizeof(void *) * (2 << (32 - immu_state->pagesize_log2))))) { fprintf(stderr, "OOM\n"); exit(1); } memset(cpu_state.dyn_pages, 0, sizeof(void *) * (2 << (32 - immu_state->pagesize_log2))); /* Register our segmentation fault handler */ sigact.sa_sigaction = dyn_sigsegv_debug; memset(&sigact.sa_mask, 0, sizeof(sigact.sa_mask)); sigact.sa_flags = SA_SIGINFO | SA_NOMASK; if(sigaction(SIGSEGV, &sigact, NULL)) printf("WARN: Unable to install SIGSEGV handler! Don't expect to be able to debug the recompiler.\n"); /* FIXME: Find a better place for this */ { /* Needed by execution */ extern int do_stats; do_stats = config.cpu.dependstats || config.cpu.superscalar || config.cpu.dependstats || config.sim.history || config.sim.exe_log; } printf("Recompile engine up and running\n"); } /* Parses instructions and their operands and populates opq with them */ static void eval_insn_ops(struct op_queue *opq, oraddr_t addr) { int breakp; struct insn_op_struct *opd; for(; opq->next; opq = opq->next, addr += 4) { opq->param_num = 0; breakp = 0; opq->insn = eval_insn(addr, &breakp); /* FIXME: If a breakpoint is set at this location, insert exception code */ if(breakp) { fprintf(stderr, "FIXME: Insert breakpoint code\n"); } opq->insn_index = insn_decode(opq->insn); if(opq->insn_index == -1) continue; opd = op_start[opq->insn_index]; do { opq->param[opq->param_num] = eval_operand_val(opq->insn, opd); opq->param_type[opq->param_num] = opd->type; opq->param_num++; while(!(opd->type & OPTYPE_OP)) opd++; } while(!(opd++->type & OPTYPE_LAST)); } } /* Adds code to the opq for the instruction pointed to by addr */ static void recompile_insn(struct op_queue *opq, int delay_insn) { int j, k; int param_t[5]; /* Which temporary the parameters reside in */ /* Check if we have an illegal instruction */ if(opq->insn_index == -1) { gen_l_invalid(opq, NULL, delay_insn); return; } /* 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[opq->insn_index].flags & OR32_IF_DELAY)) return; param_t[0] = T_NONE; param_t[1] = T_NONE; param_t[2] = T_NONE; param_t[3] = T_NONE; param_t[4] = T_NONE; /* 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(!(.& OR32_IF_DELAY)) below */ if(or32_opcodes[opq->insn_index].flags & OR32_IF_DELAY) { /* Jump instructions don't have a disposition */ or32_opcodes[opq->insn_index].exec(opq, param_t, 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. */ if((or32_opcodes[opq->insn_index].func_unit == it_exception)) { ship_gprs_out_t(opq); or32_opcodes[opq->insn_index].exec(opq, param_t, delay_insn); return; } for(j = 0; j < opq->param_num; j++) { if(!(opq->param_type[j] & OPTYPE_REG)) continue; /* Never, ever, move r0 into a temporary */ if(!opq->param[j]) continue; k = find_t(opq, opq->param[j]); param_t[j] = k; if(opq->reg_t[k] == opq->param[j]) { if(!(opq->param_type[j] & OPTYPE_DST) && !(opq->tflags[k] & TFLAG_SOURCED)) { gen_op_move_t_gpr[k][opq->reg_t[k]](opq, 0); opq->tflags[k] |= TFLAG_SOURCED; } if(opq->param_type[j] & OPTYPE_DST) opq->tflags[k] |= TFLAG_DST; else opq->tflags[k] |= TFLAG_SRC; continue; } if(opq->reg_t[k] < 32) { /* Only ship the temporary out if it has been used as a destination * register */ ship_t_out(opq, k); } if(opq->param_type[j] & OPTYPE_DST) opq->tflags[k] = TFLAG_DST; else opq->tflags[k] = TFLAG_SRC; opq->reg_t[k] = opq->param[j]; /* Only generate code to move the register into a temporary if it is used as * a source operand */ if(!(opq->param_type[j] & OPTYPE_DST)) { gen_op_move_t_gpr[k][opq->reg_t[k]](opq, 0); opq->tflags[k] |= TFLAG_SOURCED; } } /* 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 * simulated */ if(do_stats) { for(j = 0; j < opq->param_num; j++) { if(!(opq->param_type[j] & OPTYPE_DIS)) continue; if(!opq->param[j + 1]) gen_op_store_insn_ea(opq, 1, opq->param[j]); else calc_insn_ea_table[param_t[j + 1]](opq, 1, opq->param[j]); } } or32_opcodes[opq->insn_index].exec(opq, param_t, delay_insn); if(do_stats) { ship_gprs_out_t(opq); gen_op_analysis(opq, 1); } /* 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 * the cycle counter before analysis() is called, which is not how the complex * execution model does it. */ if((or32_opcodes[opq->insn_index].func_unit == it_load) || (or32_opcodes[opq->insn_index].func_unit == it_store)) gen_op_join_mem_cycles(opq, 1); /* Delay slot instructions get a special scheduler, thus don't generate it * here */ if(!delay_insn) gen_op_do_sched(opq, 1); } /* Recompiles the page associated with *dyn */ 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; /* The start of the next page */ rec_page += immu_state->pagesize; 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++) { opq->reg_t[j] = 32; /* Out-of-range registers */ opq->tflags[j] = 0; } dyn->delayr = -verify_memoryarea(rec_addr)->ops.delayr; opq->num_ops = 0; opq->num_ops_param = 0; eval_insn_ops(opq, rec_addr); /* Insert code to check if the first instruction is exeucted in a delay slot*/ gen_op_check_delay_slot(opq, 1, 0); recompile_insn(opq, 1); ship_gprs_out_t(opq); gen_op_do_sched_delay(opq, 1); gen_op_clear_delay_insn(opq, 1); gen_op_do_jump_delay(opq, 1); gen_op_do_jump(opq, 1); gen_op_mark_loc(opq, 1); for(j = 0; j < NUM_T_REGS; j++) opq->reg_t[j] = 32; /* Out-of-range registers */ for(; rec_addr < rec_page; rec_addr += 4, opq = opq->next) { 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; for(j = 0; j < NUM_T_REGS; j++) opq->tflags[j] = TFLAG_SOURCED; /* 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); for(j = 0; j < NUM_T_REGS; j++) { opq->reg_t[j] = 32; opq->prev->reg_t[j] = 32; } } } recompile_insn(opq, 0); /* Store the state of the temporaries */ memcpy(opq->next->reg_t, opq->reg_t, sizeof(opq->reg_t)); } dyn->dirty = 0; /* Ship temporaries out to the corrisponding registers */ ship_gprs_out_t(opq->prev); opq->num_ops = 0; opq->num_ops_param = 0; opq->not_jump_loc = -1; opq->jump_local = -1; /* Insert code to jump to the next page */ gen_op_do_jump(opq, 1); /* Generate the code */ gen_code(cpu_state.opqs, dyn); /* Fix up the locations */ for(loc = dyn->locs; loc < &dyn->locs[immu_state->pagesize / 4]; loc++) *loc += (unsigned int)dyn->host_page; cpu_state.opqs->ops_param[0] += (unsigned int)dyn->host_page; /* Search for page-local jumps */ opq = cpu_state.opqs; for(j = 0; j < (immu_state->pagesize / 4); opq = opq->next, j++) { if(opq->jump_local != -1) opq->ops_param[opq->jump_local] = (unsigned int)dyn->locs[opq->jump_local_loc >> 2]; if(opq->not_jump_loc != -1) opq->ops_param[opq->not_jump_loc] = (unsigned int)dyn->locs[j + 1]; /* Store the state of the temporaries into dyn->ts_bound */ dyn->ts_bound[j] = 0; 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; /* Reset for the next page to be recompiled */ opq->xref = 0; } /* Patch the relocations */ patch_relocs(cpu_state.opqs, dyn->host_page); if(do_stats) { opq = cpu_state.opqs; for(j = 0; j < (immu_state->pagesize / 4); j++, opq = opq->next) { dyn->insns[j] = opq->insn; dyn->insn_indexs[j] = opq->insn_index; } } /* FIXME: Fix the issue below in a more elegent way */ /* 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 */ runtime.sim.mem_cycles = 0; } /* Recompiles a delay-slot instruction (opq is the opq of the instruction * haveing the delay-slot) */ static void recompile_delay_insn(struct op_queue *opq) { struct op_queue delay_opq; int i; /* Setup a fake opq that looks very much like the delay slot instruction */ memcpy(&delay_opq, opq, sizeof(struct op_queue)); /* `Fix' a couple of bits */ for(i = 0; i < NUM_T_REGS; i++) delay_opq.tflags[i] = TFLAG_SOURCED; delay_opq.insn_index = opq->next->insn_index; memcpy(delay_opq.param_type, opq->next->param_type, sizeof(delay_opq.param_type)); memcpy(delay_opq.param, opq->next->param, sizeof(delay_opq.param)); delay_opq.param_num = opq->next->param_num; delay_opq.insn = opq->next->insn; delay_opq.xref = 0; delay_opq.insn_addr = opq->insn_addr + 4; delay_opq.prev = opq->prev; delay_opq.next = NULL; /* Generate the delay slot instruction */ recompile_insn(&delay_opq, 1); ship_gprs_out_t(&delay_opq); opq->num_ops = delay_opq.num_ops; opq->ops_len = delay_opq.ops_len; opq->ops = delay_opq.ops; opq->num_ops_param = delay_opq.num_ops_param; opq->ops_param_len = delay_opq.ops_param_len; opq->ops_param = delay_opq.ops_param; for(i = 0; i < NUM_T_REGS; i++) opq->reg_t[i] = 32; } /* 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) { int i; /* 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 */ if(IADDR_PAGE(j_ea) != IADDR_PAGE(opq->insn_addr)) /* We can't do anything as the j_ea (as passed to find_jump_loc) is a * VIRTUAL offset and the next physical page may not be the next VIRTUAL * page */ return 0; /* The jump is into the page currently undergoing dynamic recompilation */ /* If we haven't got to the location of the jump, everything is ok */ 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; } /* Insert temporary -> register code before the jump ea and register -> * temporary at the x-ref address */ for(i = (opq->insn_addr - j_ea) / 4; i; i--) opq = opq->prev; if(!opq->prev) /* We're at the begining of a page, no need to do anything */ return 1; /* Found location, insert code */ ship_gprs_out_t(opq->prev); for(i = 0; i < NUM_T_REGS; i++) { if(opq->prev->reg_t[i] < 32) /* FIXME: Ship temporaries in the begining of the opq that needs it */ gen_op_move_t_gpr[i][opq->prev->reg_t[i]](opq, 0); } opq->xref = 1; return 1; } static void gen_j_imm(struct op_queue *opq, oraddr_t off) { int jump_local; off <<= 2; if(IADDR_PAGE(opq->insn_addr) != IADDR_PAGE(opq->insn_addr + 4)) { gen_op_set_pc_delay_imm(opq, 1, off); gen_op_do_sched(opq, 1); return; } jump_local = find_jump_loc(opq->insn_addr + off, opq); gen_op_set_delay_insn(opq, 1); gen_op_do_sched(opq, 1); recompile_delay_insn(opq); 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) & immu_state->page_offset_mask; } 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 i; /* Ship the jump-to register out (if it exists). It requires special * handleing */ for(i = 0; i < NUM_T_REGS; i++) { if(opq->reg_t[i] == opq->param[0]) /* Ship temporary out in the last opq that used it */ ship_t_out(opq, i); } if(do_stats) gen_op_analysis(opq, 1); if(!gpr) gen_op_clear_pc_delay(opq, 1); else set_pc_delay_gpr[gpr](opq, 1); gen_op_do_sched(opq, 1); if(IADDR_PAGE(opq->insn_addr) != IADDR_PAGE(opq->insn_addr + 4)) return; recompile_delay_insn(opq); 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); gen_op_do_jump(opq, 1); } /*------------------------------[ Operation generation for an instruction ]---*/ /* FIXME: Flag setting is not done in any instruction */ /* FIXME: Since r0 is not moved into a temporary, check all arguments below! */ DEF_1T_OP(generic_gen_op, clear_t, gen_op_clear); DEF_2T_OP_NEQ(generic_gen_op, move_t_t, gen_op_move); DEF_1T_OP(imm_gen_op, mov_t_imm, gen_op_imm); DEF_2T_OP(imm_gen_op, l_add_imm_t_table, gen_op_add_imm); DEF_3T_OP(generic_gen_op, l_add_t_table, gen_op_add); void gen_l_add(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) /* Screw this, the operation shall do nothing */ return; if(!opq->param[1] && !opq->param[2]) { /* Just clear param_t[0] */ clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { if(opq->param[0] != opq->param[1]) /* This just moves a register */ move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(!opq->param[1]) { /* Check if we are moveing an immediate */ if(param_t[2] == T_NONE) { /* Yep, an immediate */ mov_t_imm[param_t[0]](opq, 1, opq->param[2]); return; } /* Just another move */ if(opq->param[0] != opq->param[2]) move_t_t[param_t[0]][param_t[2]](opq, 1); return; } /* Ok, This _IS_ an add... */ if(param_t[2] == T_NONE) /* immediate */ l_add_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_add_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_3T_OP(generic_gen_op, l_addc_t_table, gen_op_addc); void gen_l_addc(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) /* Screw this, the operation shall do nothing */ return; /* FIXME: More optimisations !! (...and immediate...) */ l_addc_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_2T_OP(imm_gen_op, l_and_imm_t_table, gen_op_and_imm); DEF_3T_OP_NEQ(generic_gen_op, l_and_t_table, gen_op_and); void gen_l_and(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) /* Screw this, the operation shall do nothing */ return; if(!opq->param[1] || !opq->param[2]) { /* Just clear param_t[0] */ clear_t[param_t[0]](opq, 1); return; } if((opq->param[0] == opq->param[1]) && (opq->param[0] == opq->param[2]) && (param_t[2] != T_NONE)) return; if(param_t[2] == T_NONE) l_and_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_and_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } void gen_l_bf(struct op_queue *opq, int param_t[3], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); /* The temporaries are expected to be shiped out after the execution of the * branch instruction wether it branched or not */ ship_gprs_out_t(opq->prev); if(IADDR_PAGE(opq->insn_addr) != IADDR_PAGE(opq->insn_addr + 4)) { gen_op_check_flag_delay(opq, 1, opq->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, opq->param[0]); } void gen_l_bnf(struct op_queue *opq, int param_t[3], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); /* The temporaries are expected to be shiped out after the execution of the * branch instruction wether it branched or not */ ship_gprs_out_t(opq->prev); if(IADDR_PAGE(opq->insn_addr) != IADDR_PAGE(opq->insn_addr + 4)) { gen_op_check_not_flag_delay(opq, 1, opq->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, opq->param[0]); } DEF_3T_OP_NEQ(generic_gen_op, l_cmov_t_table, gen_op_cmov); /* FIXME: Check if either opperand 1 or 2 is r0 */ void gen_l_cmov(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1] && !opq->param[2]) { clear_t[param_t[0]](opq, 1); return; } if((opq->param[1] == opq->param[2]) && (opq->param[0] == opq->param[1])) return; if(opq->param[1] == opq->param[2]) { move_t_t[param_t[0]][param_t[1]](opq, 1); return; } l_cmov_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } void gen_l_cust1(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust2(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust3(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust4(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust5(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust6(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust7(struct op_queue *opq, int param_t[3], int delay_slot) { } void gen_l_cust8(struct op_queue *opq, int param_t[3], int delay_slot) { } /* FIXME: All registers need to be stored before the div instructions as they * have the potenticial to cause an exception */ DEF_1T_OP(generic_gen_op, check_null_excpt, gen_op_check_null_except); DEF_1T_OP(generic_gen_op, check_null_excpt_delay, gen_op_check_null_except_delay); DEF_3T_OP(generic_gen_op, l_div_t_table, gen_op_div); void gen_l_div(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[2]) { /* There is no option. This _will_ cause an illeagal exception */ if(!delay_slot) { gen_op_illegal(opq, 1); gen_op_do_jump(opq, 1); } else { gen_op_illegal(opq, 1); gen_op_do_jump(opq, 1); } return; } if(!delay_slot) check_null_excpt[param_t[2]](opq, 1); else check_null_excpt_delay[param_t[2]](opq, 1); if(!opq->param[0]) return; if(!opq->param[1]) { /* Clear param_t[0] */ clear_t[param_t[0]](opq, 1); return; } l_div_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_3T_OP(generic_gen_op, l_divu_t_table, gen_op_divu); void gen_l_divu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[2]) { /* There is no option. This _will_ cause an illeagal exception */ if(!delay_slot) { gen_op_illegal(opq, 1); gen_op_do_jump(opq, 1); } else { gen_op_illegal(opq, 1); gen_op_do_jump(opq, 1); } return; } if(!delay_slot) check_null_excpt[param_t[2]](opq, 1); else check_null_excpt_delay[param_t[2]](opq, 1); if(!opq->param[0]) return; if(!opq->param[1]) { /* Clear param_t[0] */ clear_t[param_t[0]](opq, 1); return; } l_divu_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_2T_OP(generic_gen_op, l_extbs_t_table, gen_op_extbs); void gen_l_extbs(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } l_extbs_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_2T_OP(generic_gen_op, l_extbz_t_table, gen_op_extbz); void gen_l_extbz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } l_extbz_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_2T_OP(generic_gen_op, l_exths_t_table, gen_op_exths); void gen_l_exths(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } l_exths_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_2T_OP(generic_gen_op, l_exthz_t_table, gen_op_exthz); void gen_l_exthz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } l_exthz_t_table[param_t[0]][param_t[1]](opq, 1); } void gen_l_extws(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } if(opq->param[0] == opq->param[1]) return; /* In the 32-bit architechture this instruction reduces to a move */ move_t_t[param_t[0]][param_t[1]](opq, 1); } void gen_l_extwz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } if(opq->param[0] == opq->param[1]) return; /* In the 32-bit architechture this instruction reduces to a move */ move_t_t[param_t[0]][param_t[1]](opq, 1); } DEF_2T_OP(generic_gen_op, l_ff1_t_table, gen_op_ff1); void gen_l_ff1(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } l_ff1_t_table[param_t[0]][param_t[1]](opq, 1); } void gen_l_j(struct op_queue *opq, int param_t[3], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); gen_j_imm(opq, opq->param[0]); } void gen_l_jal(struct op_queue *opq, int param_t[3], int delay_slot) { int i; /* 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(i = 0; i < NUM_T_REGS; i++) { if(opq->reg_t[i] == LINK_REGNO) { /* Don't bother storeing the register, it is going to get clobered in this * instruction anyway */ opq->reg_t[i] = 32; break; } } /* Store the return address */ gen_op_store_link_addr_gpr(opq, 1); if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); gen_j_imm(opq, opq->param[0]); } void gen_l_jr(struct op_queue *opq, int param_t[3], int delay_slot) { gen_j_reg(opq, opq->param[0]); } void gen_l_jalr(struct op_queue *opq, int param_t[3], int delay_slot) { int i; /* 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(i = 0; i < NUM_T_REGS; i++) { if(opq->reg_t[i] == LINK_REGNO) { /* Don't bother storeing the register, it is going to get clobered in this * instruction anyway */ opq->reg_t[i] = 32; break; } } /* Store the return address */ gen_op_store_link_addr_gpr(opq, 1); gen_j_reg(opq, opq->param[0]); } /* FIXME: Optimise all load instruction when the disposition == 0 */ DEF_1T_OP(imm_gen_op, l_lbs_imm_t_table, gen_op_lbs_imm); DEF_2T_OP(imm_gen_op, l_lbs_t_table, gen_op_lbs); void gen_l_lbs(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lbs_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lbs_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_lbz_imm_t_table, gen_op_lbz_imm); DEF_2T_OP(imm_gen_op, l_lbz_t_table, gen_op_lbz); void gen_l_lbz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lbz_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lbz_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_lhs_imm_t_table, gen_op_lhs_imm); DEF_2T_OP(imm_gen_op, l_lhs_t_table, gen_op_lhs); void gen_l_lhs(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lhs_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lhs_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_lhz_imm_t_table, gen_op_lhz_imm); DEF_2T_OP(imm_gen_op, l_lhz_t_table, gen_op_lhz); void gen_l_lhz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lhz_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lhz_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_lws_imm_t_table, gen_op_lws_imm); DEF_2T_OP(imm_gen_op, l_lws_t_table, gen_op_lws); void gen_l_lws(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lws_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lws_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_lwz_imm_t_table, gen_op_lwz_imm); DEF_2T_OP(imm_gen_op, l_lwz_t_table, gen_op_lwz); void gen_l_lwz(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { /* FIXME: This will work, but the statistics need to be updated... */ return; } /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { /* Load the data from the immediate */ l_lwz_imm_t_table[param_t[0]](opq, 1, opq->param[1]); return; } l_lwz_t_table[param_t[0]][param_t[2]](opq, 1, opq->param[1]); } DEF_1T_OP(imm_gen_op, l_mac_imm_t_table, gen_op_mac_imm); DEF_2T_OP(generic_gen_op, l_mac_t_table, gen_op_mac); void gen_l_mac(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] || !opq->param[1]) return; if(param_t[1] == T_NONE) l_mac_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_mac_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_macrc_t_table, gen_op_macrc); void gen_l_macrc(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) { gen_op_macc(opq, 1); return; } l_macrc_t_table[param_t[0]](opq, 1); } DEF_1T_OP(imm_gen_op, l_mfspr_imm_t_table, gen_op_mfspr_imm); DEF_2T_OP(imm_gen_op, l_mfspr_t_table, gen_op_mfspr); void gen_l_mfspr(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { l_mfspr_imm_t_table[param_t[0]](opq, 1, opq->param[2]); return; } l_mfspr_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); } void gen_l_movhi(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } mov_t_imm[param_t[0]](opq, 1, opq->param[1] << 16); } DEF_2T_OP(generic_gen_op, l_msb_t_table, gen_op_msb); void gen_l_msb(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] || !opq->param[1]) return; l_msb_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(imm_gen_op, l_mtspr_clear_t_table, gen_op_mtspr_clear); DEF_1T_OP(imm_gen_op, l_mtspr_imm_t_table, gen_op_mtspr_imm); DEF_2T_OP(imm_gen_op, l_mtspr_t_table, gen_op_mtspr); void gen_l_mtspr(struct op_queue *opq, int param_t[3], int delay_slot) { /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[0]) { if(!opq->param[1]) { /* Clear the immediate SPR */ gen_op_mtspr_imm_clear(opq, 1, opq->param[2]); return; } l_mtspr_imm_t_table[param_t[1]](opq, 1, opq->param[2]); return; } if(!opq->param[1]) { l_mtspr_clear_t_table[param_t[0]](opq, 1, opq->param[2]); return; } l_mtspr_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); } DEF_2T_OP(imm_gen_op, l_mul_imm_t_table, gen_op_mul_imm); DEF_3T_OP(generic_gen_op, l_mul_t_table, gen_op_mul); void gen_l_mul(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1] || !opq->param[2]) { clear_t[param_t[0]](opq, 1); return; } if(param_t[2] == T_NONE) l_mul_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_mul_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_3T_OP(generic_gen_op, l_mulu_t_table, gen_op_mulu); void gen_l_mulu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1] || !opq->param[2]) { clear_t[param_t[0]](opq, 1); return; } l_mulu_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } void gen_l_nop(struct op_queue *opq, int param_t[3], int delay_slot) { /* Do parameter switch now */ switch(opq->param[0]) { case NOP_NOP: break; case NOP_EXIT: ship_gprs_out_t(opq->prev); gen_op_nop_exit(opq, 1); break; case NOP_CNT_RESET: gen_op_nop_reset(opq, 1); break; case NOP_PRINTF: ship_gprs_out_t(opq->prev); gen_op_nop_printf(opq, 1); break; case NOP_REPORT: ship_gprs_out_t(opq->prev); gen_op_nop_report(opq, 1); break; default: if((opq->param[0] >= NOP_REPORT_FIRST) && (opq->param[0] <= NOP_REPORT_LAST)) { ship_gprs_out_t(opq->prev); gen_op_nop_report_imm(opq, 1, opq->param[0] - NOP_REPORT_FIRST); } break; } } DEF_2T_OP(imm_gen_op, l_or_imm_t_table, gen_op_or_imm); DEF_3T_OP_NEQ(generic_gen_op, l_or_t_table, gen_op_or); void gen_l_or(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if((opq->param[0] == opq->param[1]) && (opq->param[0] == opq->param[2]) && (param_t[2] != T_NONE)) return; if(!opq->param[1] && !opq->param[2]) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { if((param_t[2] == T_NONE) && (opq->param[0] == opq->param[1])) return; move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(!opq->param[1]) { /* Check if we are moveing an immediate */ if(param_t[2] == T_NONE) { /* Yep, an immediate */ mov_t_imm[param_t[0]](opq, 1, opq->param[2]); return; } /* Just another move */ move_t_t[param_t[0]][param_t[2]](opq, 1); return; } if(param_t[2] == T_NONE) l_or_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_or_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } void gen_l_rfe(struct op_queue *opq, int param_t[3], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); gen_op_prep_rfe(opq, 1); /* FIXME: rename op_do_sched_delay */ gen_op_do_sched_delay(opq, 1); gen_op_do_jump(opq, 1); } /* FIXME: All store instructions should be optimised when the disposition = 0 */ DEF_1T_OP(imm_gen_op, l_sb_clear_table, gen_op_sb_clear); DEF_1T_OP(imm_gen_op, l_sb_imm_t_table, gen_op_sb_imm); DEF_2T_OP(imm_gen_op, l_sb_t_table, gen_op_sb); void gen_l_sb(struct op_queue *opq, int param_t[3], int delay_slot) { /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { if(!opq->param[1]) { gen_op_sb_clear_imm(opq, 1, opq->param[0]); return; } l_sb_clear_table[param_t[1]](opq, 1, opq->param[0]); return; } if(!opq->param[1]) { /* Store the data to the immediate */ l_sb_imm_t_table[param_t[2]](opq, 1, opq->param[0]); return; } l_sb_t_table[param_t[1]][param_t[2]](opq, 1, opq->param[0]); } DEF_1T_OP(imm_gen_op, l_sh_clear_table, gen_op_sh_clear); DEF_1T_OP(imm_gen_op, l_sh_imm_t_table, gen_op_sh_imm); DEF_2T_OP(imm_gen_op, l_sh_t_table, gen_op_sh); void gen_l_sh(struct op_queue *opq, int param_t[3], int delay_slot) { /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { if(!opq->param[1]) { gen_op_sh_clear_imm(opq, 1, opq->param[0]); return; } l_sh_clear_table[param_t[1]](opq, 1, opq->param[0]); return; } if(!opq->param[1]) { /* Store the data to the immediate */ l_sh_imm_t_table[param_t[2]](opq, 1, opq->param[0]); return; } l_sh_t_table[param_t[1]][param_t[2]](opq, 1, opq->param[0]); } DEF_1T_OP(imm_gen_op, l_sw_clear_table, gen_op_sw_clear); DEF_1T_OP(imm_gen_op, l_sw_imm_t_table, gen_op_sw_imm); DEF_2T_OP(imm_gen_op, l_sw_t_table, gen_op_sw); void gen_l_sw(struct op_queue *opq, int param_t[3], int delay_slot) { /* Just in case an exception happens */ ship_gprs_out_t(opq->prev); if(!opq->param[2]) { if(!opq->param[1]) { gen_op_sw_clear_imm(opq, 1, opq->param[0]); return; } l_sw_clear_table[param_t[1]](opq, 1, opq->param[0]); return; } if(!opq->param[1]) { /* Store the data to the immediate */ l_sw_imm_t_table[param_t[2]](opq, 1, opq->param[0]); return; } l_sw_t_table[param_t[1]][param_t[2]](opq, 1, opq->param[0]); } DEF_1T_OP(generic_gen_op, l_sfeq_null_t_table, gen_op_sfeq_null); DEF_1T_OP(imm_gen_op, l_sfeq_imm_t_table, gen_op_sfeq_imm); DEF_2T_OP(generic_gen_op, l_sfeq_t_table, gen_op_sfeq); void gen_l_sfeq(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { if(param_t[1] == T_NONE) { if(!opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfeq_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfeq_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfeq_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfeq_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_sfges_null_t_table, gen_op_sfges_null); DEF_1T_OP(generic_gen_op, l_sfles_null_t_table, gen_op_sfles_null); DEF_1T_OP(imm_gen_op, l_sfges_imm_t_table, gen_op_sfges_imm); DEF_2T_OP(generic_gen_op, l_sfges_t_table, gen_op_sfges); void gen_l_sfges(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { /* sfles IS correct */ if(param_t[1] == T_NONE) { if(0 >= (orreg_t)opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfles_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfges_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfges_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfges_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_sfgeu_null_t_table, gen_op_sfgeu_null); DEF_1T_OP(generic_gen_op, l_sfleu_null_t_table, gen_op_sfleu_null); DEF_1T_OP(imm_gen_op, l_sfgeu_imm_t_table, gen_op_sfgeu_imm); DEF_2T_OP(generic_gen_op, l_sfgeu_t_table, gen_op_sfgeu); void gen_l_sfgeu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { /* sfleu IS correct */ if(param_t[1] == T_NONE) { if(0 >= opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfleu_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfgeu_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfgeu_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfgeu_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_sfgts_null_t_table, gen_op_sfgts_null); DEF_1T_OP(generic_gen_op, l_sflts_null_t_table, gen_op_sflts_null); DEF_1T_OP(imm_gen_op, l_sfgts_imm_t_table, gen_op_sfgts_imm); DEF_2T_OP(generic_gen_op, l_sfgts_t_table, gen_op_sfgts); void gen_l_sfgts(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_clear_flag(opq, 1); return; } if(!opq->param[0]) { /* sflts IS correct */ if(param_t[1] == T_NONE) { if(0 > (orreg_t)opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sflts_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfgts_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfgts_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfgts_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_sfgtu_null_t_table, gen_op_sfgtu_null); DEF_1T_OP(generic_gen_op, l_sfltu_null_t_table, gen_op_sfltu_null); DEF_1T_OP(imm_gen_op, l_sfgtu_imm_t_table, gen_op_sfgtu_imm); DEF_2T_OP(generic_gen_op, l_sfgtu_t_table, gen_op_sfgtu); void gen_l_sfgtu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_clear_flag(opq, 1); return; } if(!opq->param[0]) { /* sfltu IS correct */ if(param_t[1] == T_NONE) { if(0 > opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfltu_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfgtu_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfgtu_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfgtu_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(imm_gen_op, l_sfles_imm_t_table, gen_op_sfles_imm); DEF_2T_OP(generic_gen_op, l_sfles_t_table, gen_op_sfles); void gen_l_sfles(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { /* sfges IS correct */ if(param_t[1] == T_NONE) { if(0 <= (orreg_t)opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfges_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfles_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfles_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfles_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(imm_gen_op, l_sfleu_imm_t_table, gen_op_sfleu_imm); DEF_2T_OP(generic_gen_op, l_sfleu_t_table, gen_op_sfleu); void gen_l_sfleu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { /* sfleu IS correct */ if(param_t[1] == T_NONE) { if(0 <= opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfgeu_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfleu_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfleu_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfleu_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(imm_gen_op, l_sflts_imm_t_table, gen_op_sflts_imm); DEF_2T_OP(generic_gen_op, l_sflts_t_table, gen_op_sflts); void gen_l_sflts(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_clear_flag(opq, 1); return; } if(!opq->param[0]) { /* sfgts IS correct */ if(param_t[1] == T_NONE) { if(0 < (orreg_t)opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfgts_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sflts_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sflts_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sflts_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(imm_gen_op, l_sfltu_imm_t_table, gen_op_sfltu_imm); DEF_2T_OP(generic_gen_op, l_sfltu_t_table, gen_op_sfltu); void gen_l_sfltu(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_clear_flag(opq, 1); return; } if(!opq->param[0]) { /* sfgtu IS correct */ if(param_t[1] == T_NONE) { if(0 < opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); } else l_sfgtu_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfltu_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfltu_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfltu_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_1T_OP(generic_gen_op, l_sfne_null_t_table, gen_op_sfne_null); DEF_1T_OP(imm_gen_op, l_sfne_imm_t_table, gen_op_sfne_imm); DEF_2T_OP(generic_gen_op, l_sfne_t_table, gen_op_sfne); void gen_l_sfne(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0] && !opq->param[1]) { gen_op_set_flag(opq, 1); return; } if(!opq->param[0]) { if(param_t[1] == T_NONE) if(opq->param[1]) gen_op_set_flag(opq, 1); else gen_op_clear_flag(opq, 1); else l_sfne_null_t_table[param_t[1]](opq, 1); return; } if(!opq->param[1]) { l_sfne_null_t_table[param_t[0]](opq, 1); return; } if(param_t[1] == T_NONE) l_sfne_imm_t_table[param_t[0]](opq, 1, opq->param[1]); else l_sfne_t_table[param_t[0]][param_t[1]](opq, 1); } DEF_2T_OP(imm_gen_op, l_sll_imm_t_table, gen_op_sll_imm); DEF_3T_OP(generic_gen_op, l_sll_t_table, gen_op_sll); void gen_l_sll(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(param_t[2] == T_NONE) l_sll_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_sll_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_2T_OP(imm_gen_op, l_sra_imm_t_table, gen_op_sra_imm); DEF_3T_OP(generic_gen_op, l_sra_t_table, gen_op_sra); void gen_l_sra(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(param_t[2] == T_NONE) l_sra_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_sra_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_2T_OP(imm_gen_op, l_srl_imm_t_table, gen_op_srl_imm); DEF_3T_OP(generic_gen_op, l_srl_t_table, gen_op_srl); void gen_l_srl(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if(!opq->param[1]) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(param_t[2] == T_NONE) l_srl_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_srl_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } DEF_2T_OP(generic_gen_op, l_neg_t_table, gen_op_neg); DEF_3T_OP(generic_gen_op, l_sub_t_table, gen_op_sub); void gen_l_sub(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if((param_t[2] != T_NONE) && (opq->param[1] == opq->param[2])) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[1] && !opq->param[2]) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[1]) { if(param_t[2] == T_NONE) mov_t_imm[param_t[0]](opq, 1, -opq->param[2]); else l_neg_t_table[param_t[0]][param_t[2]](opq, 1); return; } if(!opq->param[2]) { move_t_t[param_t[0]][param_t[1]](opq, 1); return; } l_sub_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } /* 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], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); if(!delay_slot) gen_op_prep_sys(opq, 1); else 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 */ void gen_l_trap(struct op_queue *opq, int param_t[3], int delay_slot) { if(do_stats) /* All gprs are current since this insn doesn't touch any reg */ gen_op_analysis(opq, 1); if(!delay_slot) gen_op_prep_trap(opq, 1); else gen_op_prep_trap_delay(opq, 1); } DEF_2T_OP(imm_gen_op, l_xor_imm_t_table, gen_op_xor_imm); /* FIXME: Make unused elements NULL */ DEF_3T_OP_NEQ(generic_gen_op, l_xor_t_table, gen_op_xor); void gen_l_xor(struct op_queue *opq, int param_t[3], int delay_slot) { if(!opq->param[0]) return; if((param_t[2] != T_NONE) && (opq->param[1] == opq->param[2])) { clear_t[param_t[0]](opq, 1); return; } if(!opq->param[2]) { if((param_t[2] == T_NONE) && (opq->param[0] == opq->param[1])) return; move_t_t[param_t[0]][param_t[1]](opq, 1); return; } if(!opq->param[1]) { if(param_t[2] == T_NONE) { mov_t_imm[param_t[0]](opq, 1, opq->param[2]); return; } move_t_t[param_t[0]][param_t[2]](opq, 1); return; } if(param_t[2] == T_NONE) l_xor_imm_t_table[param_t[0]][param_t[1]](opq, 1, opq->param[2]); else l_xor_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1); } void gen_l_invalid(struct op_queue *opq, int param_t[3], int delay_slot) { if(!delay_slot) { gen_op_illegal(opq, 1); gen_op_do_jump(opq, 1); } else { gen_op_illegal_delay(opq, 1); gen_op_do_jump(opq, 1); } } /*----------------------------------[ Floating point instructions (stubs) ]---*/ void gen_lf_add_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_div_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_ftoi_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_itof_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_madd_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_mul_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_rem_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sfeq_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sfge_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sfgt_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sfle_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sflt_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sfne_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); } void gen_lf_sub_s(struct op_queue *opq, int param_t[3], int delay_slot) { gen_l_invalid(opq, param_t, delay_slot); }
Go to most recent revision | Compare with Previous | Blame | View Log