summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/cpu/drcfe.cpp
blob: a09ae5c2890795715386e5e195e1555a56fddd29 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                





                                                                            

                                                                            

                                

                                                                  
 

                                                                            
                


                  

           


                                                                            
 
                                    


 


                                                                            
 

                                                   
 

                                     

  

                        
 
 


                                                                            
 


                                                   
 







                                                                                             
 


 


                                                   
 
                             
 

                                                     


 
                                                   

                                               

                                                   
 
                                                              
 

                                                     
 
                                          

                                                 



                                       
                                           

                                                                                      

                                         
                                                                                          
                                                              
                                                                                
                                       


                                                                  

                                                                                                          

                                                                                                   
                                              


                                 
                                               
                                                                                                                                                                                
                 

                                                                                             
 
                                                                                    


                                                                          
                                                      


                                                                        

                                                                                                        

                                                                                                   
                                                                                                        







                                                                                                                                                                     
                                                    




                                                                 




                                                                               


 




                                                   
 
                                                                                                      
 
                                     
                                                           

                               
                            


                                               
                                                        


                             

                                                               
                            
                            



                                                         
 

                                                       




                                                                                   

                                                                                                    

                                                                                
                         
                                                                             
 
                                                                      

                                           
                                                       
                                                      








                                                                                                                                            
                                         
                                                                           
                 
                                                                    
                                                                                   
                                                 
                                      

                                                       
 
                                                                    
                                                 
 

                                                                          

                                      

                                                     

                 



                    



                                                   
 
                                                                  
 
                                                                                   


                            
                                                           
                                                     
                 


                                                                                    
                                                                                                          
                                                                                                            

                                                                             
                                                                                                     

                         
                                                                                         

                                                             
                                                                                                     
                                                                                                   
                                                   
                         
 
                                                                                                  
                                                




                                                                                            
 
                                                                                    

                            
                                                                                                                               
                                                
                                                                

                                                                                                 
                                                                             
                                                                                        




                                                                              
                                                                                                                       



                                                                              

                                                                                           



                                                                      
                                                                           

                                                                 
                                                                                                                 
                                                                                                    
                                                   
                                                                                                          
                                                                                      
                                                                                                                        
 
                                                            
                                              

                         

                                                                                                                   


                                                                                        
                                                                                                               
                                           
                                                                  
                            
                                                                   
 

                                                                                           





                                                               

                                                                                 


 




                                                   
 
                                                                                 
 
                                         
                                          
                                                                            
 

                                                             
                                                                               
 
















                                                                                               


 




                                                   
 
                                         
 
                                        
                                                                                                            
                                                             
 

                                                       
 
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    drcfe.c

    Generic dynamic recompiler frontend structures and utilities.

****************************************************************************

    Future improvements/changes:

    * more aggressive handling of needed registers for conditional
        intrablock branches

***************************************************************************/

#include "emu.h"
#include "drcfe.h"


namespace {

//**************************************************************************
//  CONSTANTS
//**************************************************************************

constexpr u32 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;
};

} // anonymous namespace



//**************************************************************************
//  DRC FRONTEND
//**************************************************************************

//-------------------------------------------------
//  drc_frontend - constructor
//-------------------------------------------------

drc_frontend::drc_frontend(device_t &cpu, u32 window_start, u32 window_end, u32 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_array(window_end + window_start + 2, nullptr)
{
}


//-------------------------------------------------
//  ~drc_frontend - destructor
//-------------------------------------------------

drc_frontend::~drc_frontend()
{
	// release any descriptions we've accumulated
	release_descriptions();
}


//-------------------------------------------------
//  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 const minpc = startpc - (std::min)(m_window_start, startpc);
	offs_t const maxpc = startpc + (std::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 *const curstack = --pcstackptr;
		opcode_desc *curdesc = m_desc_array[curstack->targetpc - minpc];
		if (curdesc != nullptr)
		{
			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] == nullptr; 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, opcode_desc const *prevdesc, bool in_delay_slot)
{
	// initialize the description
	opcode_desc *const desc = m_desc_allocator.alloc();
	desc->m_next = nullptr;
	desc->branch = nullptr;
	desc->delay.reset();
	desc->pc = curpc;
	desc->physpc = curpc;
	desc->targetpc = BRANCH_TARGET_DYNAMIC;
	memset(&desc->opptr, 0x00, sizeof(desc->opptr));
	desc->length = 0;
	desc->delayslots = 0;
	desc->skipslots = 0;
	// set the delay slot flag
	desc->flags = in_delay_slot ? OPFLAG_IN_DELAY_SLOT : 0;
	desc->userflags = 0;
	desc->userdata0 = 0;
	desc->cycles = 0;
	memset(desc->regin, 0x00, sizeof(desc->regin));
	memset(desc->regout, 0x00, sizeof(desc->regout));
	memset(desc->regreq, 0x00, sizeof(desc->regreq));

	// 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;
		// If this is a delay slot it is the true branch fork and the pc should be the previous branch target
		if (desc->flags & OPFLAG_IN_DELAY_SLOT) {
			if (prevdesc->targetpc != BRANCH_TARGET_DYNAMIC) {
				delaypc = prevdesc->targetpc;
				//printf("drc_frontend::describe_one Branch in delay slot. curpc=%08X delaypc=%08X\n", curpc, delaypc);
			} else {
				//printf("drc_frontend::describe_one Warning! Branch in delay slot of dynamic target. curpc=%08X\n", curpc);
			}
		}
		opcode_desc *prev = desc;
		for (u8 slotnum = 0; slotnum < desc->delayslots; slotnum++)
		{
			// recursively describe the next instruction
			opcode_desc *delaydesc = describe_one(delaypc, prev, true);
			if (delaydesc == nullptr)
				break;
			desc->delay.append(*delaydesc);
			prev = desc;

			// set a pointer back to the original branch
			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, u32 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] != nullptr)
		{
			// 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] : nullptr;
			for (u8 skipnum = 0; skipnum < curdesc->skipslots && nextdesc != nullptr; skipnum++)
			{
				nextdescnum = nextdescnum + nextdesc->length;
				nextdesc = (nextdescnum < end) ? m_desc_array[nextdescnum] : nullptr;
			}

			// 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 == nullptr)
			{
				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 = nullptr;
				for (scandescnum = descnum + 1; scandescnum < end; scandescnum++)
				{
					scandesc = m_desc_array[scandescnum];
					if (scandesc != nullptr || 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
				u32 reqmask[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
				if (seqstart != -1)
					for (int backdesc = descnum; backdesc != seqstart - 1; backdesc--)
						if (m_desc_array[backdesc] != nullptr)
							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, u32 *reqmask)
{
	// recursively handle delay slots
	if (desc.delay.first() != nullptr)
		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 != nullptr; curdesc = curdesc->next())
		m_desc_allocator.reclaim_all(curdesc->delay);

	// reclaim all the descriptors
	m_desc_allocator.reclaim_all(m_desc_live_list);
}