diff options
Diffstat (limited to 'trunk/src/emu/cpu/drcfe.c')
-rw-r--r-- | trunk/src/emu/cpu/drcfe.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/drcfe.c b/trunk/src/emu/cpu/drcfe.c new file mode 100644 index 00000000000..03000e151a9 --- /dev/null +++ b/trunk/src/emu/cpu/drcfe.c @@ -0,0 +1,401 @@ +/*************************************************************************** + + drcfe.c + + Generic dynamic recompiler frontend structures and utilities. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +**************************************************************************** + + Future improvements/changes: + + * more aggressive handling of needed registers for conditional + intrablock branches + +***************************************************************************/ + +#include "emu.h" +#include "drcfe.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +const UINT32 MAX_STACK_DEPTH = 100; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// an entry that maps branches for our code walking +struct pc_stack_entry +{ + offs_t targetpc; + offs_t srcpc; +}; + + + +//************************************************************************** +// DRC FRONTEND +//************************************************************************** + +//------------------------------------------------- +// drc_frontend - constructor +//------------------------------------------------- + +drc_frontend::drc_frontend(device_t &cpu, UINT32 window_start, UINT32 window_end, UINT32 max_sequence) + : m_window_start(window_start), + m_window_end(window_end), + m_max_sequence(max_sequence), + m_cpudevice(downcast<cpu_device &>(cpu)), + m_program(m_cpudevice.space(AS_PROGRAM)), + m_pageshift(m_cpudevice.space_config(AS_PROGRAM)->m_page_shift), + m_desc_live_list(cpu.machine().respool()), + m_desc_allocator(cpu.machine().respool()), + m_desc_array(auto_alloc_array_clear(cpu.machine(), opcode_desc *, window_end + window_start + 2)) +{ +} + + +//------------------------------------------------- +// ~drc_frontend - destructor +//------------------------------------------------- + +drc_frontend::~drc_frontend() +{ + // release any descriptions we've accumulated + release_descriptions(); + + // free the description array + auto_free(m_cpudevice.machine(), m_desc_array); +} + + +//------------------------------------------------- +// describe_code - describe a sequence of code +// that falls within the configured window +// relative to the specified startpc +//------------------------------------------------- + +const opcode_desc *drc_frontend::describe_code(offs_t startpc) +{ + // release any descriptions we've accumulated + release_descriptions(); + + // add the initial PC to the stack + pc_stack_entry pcstack[MAX_STACK_DEPTH]; + pc_stack_entry *pcstackptr = &pcstack[0]; + pcstackptr->srcpc = 0; + pcstackptr->targetpc = startpc; + pcstackptr++; + + // loop while we still have a stack + offs_t minpc = startpc - MIN(m_window_start, startpc); + offs_t maxpc = startpc + MIN(m_window_end, 0xffffffff - startpc); + while (pcstackptr != &pcstack[0]) + { + // if we've already hit this PC, just mark it a branch target and continue + pc_stack_entry *curstack = --pcstackptr; + opcode_desc *curdesc = m_desc_array[curstack->targetpc - minpc]; + if (curdesc != NULL) + { + curdesc->flags |= OPFLAG_IS_BRANCH_TARGET; + + // if the branch crosses a page boundary, mark the target as needing to revalidate + if (m_pageshift != 0 && ((curstack->srcpc ^ curdesc->pc) >> m_pageshift) != 0) + curdesc->flags |= OPFLAG_VALIDATE_TLB | OPFLAG_CAN_CAUSE_EXCEPTION; + + // continue processing + continue; + } + + // loop until we exit the block + for (offs_t curpc = curstack->targetpc; curpc >= minpc && curpc < maxpc && m_desc_array[curpc - minpc] == NULL; curpc += m_desc_array[curpc - minpc]->length) + { + // allocate a new description and describe this instruction + m_desc_array[curpc - minpc] = curdesc = describe_one(curpc, curdesc); + + // first instruction in a sequence is always a branch target + if (curpc == curstack->targetpc) + curdesc->flags |= OPFLAG_IS_BRANCH_TARGET; + + // stop if we hit a page fault + if (curdesc->flags & OPFLAG_COMPILER_PAGE_FAULT) + break; + + // if we are the first instruction in the whole window, we must validate the TLB + if (curpc == startpc && m_pageshift != 0) + curdesc->flags |= OPFLAG_VALIDATE_TLB | OPFLAG_CAN_CAUSE_EXCEPTION; + + // if we are a branch within the block range, add the branch target to our stack + if ((curdesc->flags & OPFLAG_IS_BRANCH) && curdesc->targetpc >= minpc && curdesc->targetpc < maxpc && pcstackptr < &pcstack[MAX_STACK_DEPTH]) + { + curdesc->flags |= OPFLAG_INTRABLOCK_BRANCH; + pcstackptr->srcpc = curdesc->pc; + pcstackptr->targetpc = curdesc->targetpc; + pcstackptr++; + } + + // if we're done, we're done + if (curdesc->flags & OPFLAG_END_SEQUENCE) + break; + } + } + + // now build the list of descriptions in order + // first from startpc -> maxpc, then from minpc -> startpc + build_sequence(startpc - minpc, maxpc - minpc, OPFLAG_REDISPATCH); + build_sequence(minpc - minpc, startpc - minpc, OPFLAG_RETURN_TO_START); + return m_desc_live_list.first(); +} + + +//------------------------------------------------- +// describe_one - describe a single instruction, +// recursively describing opcodes in delay +// slots of branches as well +//------------------------------------------------- + +opcode_desc *drc_frontend::describe_one(offs_t curpc, const opcode_desc *prevdesc) +{ + // initialize the description + opcode_desc *desc = m_desc_allocator.alloc(); + memset(desc, 0, sizeof(*desc)); + desc->pc = curpc; + desc->physpc = curpc; + desc->targetpc = BRANCH_TARGET_DYNAMIC; + + // call the callback to describe an instruction + if (!describe(*desc, prevdesc)) + { + desc->flags |= OPFLAG_WILL_CAUSE_EXCEPTION | OPFLAG_INVALID_OPCODE; + return desc; + } + + // validate the TLB if we are exactly at the start of a page, or if we cross a page boundary + if (m_pageshift != 0 && (((curpc - 1) ^ (curpc + desc->length - 1)) >> m_pageshift) != 0) + desc->flags |= OPFLAG_VALIDATE_TLB | OPFLAG_CAN_CAUSE_EXCEPTION; + + // validate stuff + assert(desc->length > 0 || (desc->flags & OPFLAG_VIRTUAL_NOOP) != 0); + + // if we are a branch with delay slots, recursively walk those + if (desc->flags & OPFLAG_IS_BRANCH) + { + // iterate over slots and describe them + offs_t delaypc = curpc + desc->length; + opcode_desc *prev = desc; + for (UINT8 slotnum = 0; slotnum < desc->delayslots; slotnum++) + { + // recursively describe the next instruction + opcode_desc *delaydesc = describe_one(delaypc, prev); + if (delaydesc == NULL) + break; + desc->delay.append(*delaydesc); + prev = desc; + + // set the delay slot flag and a pointer back to the original branch + delaydesc->flags |= OPFLAG_IN_DELAY_SLOT; + delaydesc->branch = desc; + + // stop if we hit a page fault + if (delaydesc->flags & OPFLAG_COMPILER_PAGE_FAULT) + break; + + // otherwise, advance + delaypc += delaydesc->length; + } + } + return desc; +} + + +//------------------------------------------------- +// build_sequence - build an ordered sequence +// of instructions +//------------------------------------------------- + +void drc_frontend::build_sequence(int start, int end, UINT32 endflag) +{ + // iterate in order from start to end, picking up all non-NULL instructions + int consecutive = 0; + int seqstart = -1; + int skipsleft = 0; + for (int descnum = start; descnum < end; descnum++) + if (m_desc_array[descnum] != NULL) + { + // determine the next instruction, taking skips into account + opcode_desc *curdesc = m_desc_array[descnum]; + int nextdescnum = descnum + curdesc->length; + opcode_desc *nextdesc = (nextdescnum < end) ? m_desc_array[nextdescnum] : NULL; + for (UINT8 skipnum = 0; skipnum < curdesc->skipslots && nextdesc != NULL; skipnum++) + { + nextdescnum = nextdescnum + nextdesc->length; + nextdesc = (nextdescnum < end) ? m_desc_array[nextdescnum] : NULL; + } + + // start a new sequence if we aren't already in the middle of one + if (seqstart == -1 && skipsleft == 0) + { + // tag all start-of-sequence instructions as needing TLB verification + curdesc->flags |= OPFLAG_VALIDATE_TLB | OPFLAG_CAN_CAUSE_EXCEPTION; + seqstart = descnum; + } + + // if we are the last instruction, indicate end-of-sequence and redispatch + if (nextdesc == NULL) + { + curdesc->flags |= OPFLAG_END_SEQUENCE; + if (endflag != OPFLAG_RETURN_TO_START || nextdescnum == end) + curdesc->flags |= endflag; + } + + // otherwise, do some analysis based on the next instruction + else + { + // if there are instructions between us and the next instruction, we must end our sequence here + int scandescnum; + opcode_desc *scandesc = NULL; + for (scandescnum = descnum + 1; scandescnum < end; scandescnum++) + { + scandesc = m_desc_array[scandescnum]; + if (scandesc != NULL || scandesc == nextdesc) + break; + } + if (scandesc != nextdesc) + curdesc->flags |= OPFLAG_END_SEQUENCE; + + // if the next instruction is a branch target, mark this instruction as end of sequence + if (nextdesc->flags & OPFLAG_IS_BRANCH_TARGET) + curdesc->flags |= OPFLAG_END_SEQUENCE; + } + + // if we exceed the maximum consecutive count, cut off the sequence + if (++consecutive >= m_max_sequence) + curdesc->flags |= OPFLAG_END_SEQUENCE; + if (curdesc->flags & OPFLAG_END_SEQUENCE) + consecutive = 0; + + // if this is the end of a sequence, work backwards + if (curdesc->flags & OPFLAG_END_SEQUENCE) + { + // figure out which registers we *must* generate, assuming at the end all must be + UINT32 reqmask[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + if (seqstart != -1) + for (int backdesc = descnum; backdesc != seqstart - 1; backdesc--) + if (m_desc_array[backdesc] != NULL) + accumulate_required_backwards(*m_desc_array[backdesc], reqmask); + + // reset the register states + seqstart = -1; + } + + // if we have instructions remaining to be skipped, and this instruction is a branch target + // belay the skip order + if (skipsleft > 0 && (curdesc->flags & OPFLAG_IS_BRANCH_TARGET)) + skipsleft = 0; + + // if we're not getting skipped, add us to the end of the list and clear our array slot + if (skipsleft == 0) + m_desc_live_list.append(*curdesc); + else + m_desc_allocator.reclaim(*curdesc); + + // if the current instruction starts skipping, reset our skip count + // otherwise, just decrement + if (curdesc->skipslots > 0) + skipsleft = curdesc->skipslots; + else if (skipsleft > 0) + skipsleft--; + } + + // zap the array + memset(&m_desc_array[start], 0, (end - start) * sizeof(m_desc_array[0])); +} + + +//------------------------------------------------- +// accumulate_required_backwards - recursively +// accumulate live register liveness information +// walking in a backwards direction +//------------------------------------------------- + +void drc_frontend::accumulate_required_backwards(opcode_desc &desc, UINT32 *reqmask) +{ + // recursively handle delay slots + if (desc.delay.first() != NULL) + accumulate_required_backwards(*desc.delay.first(), reqmask); + + // if this is a branch, we have to reset our requests + if (desc.flags & OPFLAG_IS_BRANCH) + reqmask[0] = reqmask[1] = reqmask[2] = reqmask[3] = 0xffffffff; + + // determine the required registers + desc.regreq[0] = desc.regout[0] & reqmask[0]; + desc.regreq[1] = desc.regout[1] & reqmask[1]; + desc.regreq[2] = desc.regout[2] & reqmask[2]; + desc.regreq[3] = desc.regout[3] & reqmask[3]; + + // any registers modified by this instruction aren't required upstream until referenced + reqmask[0] &= ~desc.regout[0]; + reqmask[1] &= ~desc.regout[1]; + reqmask[2] &= ~desc.regout[2]; + reqmask[3] &= ~desc.regout[3]; + + // any registers required by this instruction now get marked required + reqmask[0] |= desc.regin[0]; + reqmask[1] |= desc.regin[1]; + reqmask[2] |= desc.regin[2]; + reqmask[3] |= desc.regin[3]; +} + + +//------------------------------------------------- +// release_descriptions - release any +// descriptions we've allocated back to the +// free list +//------------------------------------------------ + +void drc_frontend::release_descriptions() +{ + // release all delay slots first + for (opcode_desc *curdesc = m_desc_live_list.first(); curdesc != NULL; curdesc = curdesc->next()) + m_desc_allocator.reclaim_all(curdesc->delay); + + // reclaim all the descriptors + m_desc_allocator.reclaim_all(m_desc_live_list); +} |