// Optimiser.cpp: implementation of the COptimiser class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "archimedes.h"
#include "Optimiser.h"
#include "ArmletDisassembler.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

COptimiser::COptimiser()
{
	TRACE("construct COptimiser \n");

	BOOL armletSuccess = armletReportFile.Open(
		_T("D:\\Work\\Project\\armletDiss.txt"),
		CFile::modeCreate		|
		CFile::modeWrite		|
		CFile::shareDenyWrite	|
		CFile::typeText
		);

	generator = new CGenerator();
}

COptimiser::~COptimiser()
{
	delete generator;
}

//
// takes a linked list of armlets forming a chunk and optimises them
// passing them on to the generator when finished
//

void COptimiser::optimise(CLinkedList *chunkList)
{
	
	

	// ??? perform constant folding

	// ??? perform any other work that needs doing before
	// adding/removing armlets becomes expensive (i.e. before linked list destroyed)



	// create array of armlets for fast access

	// get number of armlets in the linked list
	totalArmlets = chunkList->numberOfElements();
	chunk = new Armlet*[ totalArmlets ];
	// remove pointers from list and put them in array
	int index = 0;
	while( !chunkList->empty() )
	{
		chunk[index] = (Armlet*)chunkList->removeHead();
		index++;
		// ??? debugging
		TRACE("armlet index=%d added \n", index);
	}

	// perform basic block analysis
	identifyLeaders();

	// remove redundant flag calculations
	TRACE("REDUNDANT FLAG REMOVAL IS DISABLED\n");
	// ???
	//removeRedundantFlagCalculations();

	// ??? disassemble debugging
	CArmletDisassembler diss;
	TRACE("list post redundant flag removal\n");
	for(int i=0; i<totalArmlets; i++)
	{
		TRACE("0x%x: %s \n", i, diss.disassemble(i, chunk[i]) );

		CString armletDump;
		armletDump.Format("0x%x: %s \n", i, diss.disassemble(i, chunk[i]) );
		armletReportFile.WriteString(armletDump);
	}

	// pass armlet chunk onto generator                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
	generator->generate(chunk, totalArmlets);

	// ??? delete chunk of armlets
	// delete []chunk;
}

//
// Mark all the armlets which are leaders of a basic block as such
//

void COptimiser::identifyLeaders()
{
	// force first armlet in block to be a leader (as it must be)
	chunk[0]->leader = TRUE;

	// examine all the armlets
	for(int armlet=0; armlet<totalArmlets; armlet++)
	{
		// if this armlet is a goto
		uint8 opcode = chunk[armlet]->opcode;
		if( (opcode >= irGOTOEQ) && (opcode <=irGOTO) )
		{
			// get destination of goto
			uint32 destination = chunk[armlet]->value;
			// set the destination of the goto to be a leader
			chunk[destination]->leader = TRUE;
			// set armlet following this goto (if there is one) to be a leader
			if(armlet+1 < totalArmlets)
				chunk[armlet+1]->leader = TRUE;
		}
		else
		{
			// if opcode is a leave
			if(opcode==irLEAVE)
			{
				// set armlet following this leave (if there is one) to be a leader
				if(armlet+1 < totalArmlets)
					chunk[armlet+1]->leader = TRUE;
			}
		}
	}
}

//
// Remove any outflags settings that are never utilised by inflags
// because they are overwritten by a later outflags. This will also
// leave settings ok for the end of the basic block.
//

void COptimiser::removeRedundantFlagCalculations()
{

	BOOL flagChanged[4];	// details whether the flag has changed needlessly
	uint32 flagSetBy[4];	// details which armlet output the flag

	// go through all armlets
	for(int armlet = 0; armlet<totalArmlets; armlet++)
	{
		// if this armlet is the leader of a basic block then reset to scratch
		if( chunk[armlet]->leader )
		{
			// clear information
			for(int flag=0; flag<4; flag++)
			{
				flagChanged[flag] = FALSE;
				flagSetBy[flag] = -1;
			}
		}

		// check inflags
		for(int inflag=0; inflag<4; inflag++)
		{
			// if this armlet requires this flag to be set correctly
			if( chunk[armlet]->inflags & (1<<inflag) )
			{
				flagChanged[inflag] = FALSE;
			}
		}

		// check outflags

		// for each flag
		for(int outflag=0; outflag<4; outflag++)
		{
			// if the flag is output by the current armlet
			if( chunk[armlet]->outflags & (1<<outflag) )
			{
				// if that flag has already been changed (without being required)
				if( flagChanged[outflag] )
				{
					// then stop previous instruction generating this flag
					// since it's not been used between now and when this
					// armlet is changing it
					chunk[ flagSetBy[outflag] ]->outflags &= ~(1<<outflag);
					// flagChanged[flag] remains true
				}
				else
				{
					// this armlet has changed this flag so
					flagChanged[ outflag ] = TRUE;
				}

				// update flagSetBy to point to current armlet
				flagSetBy[ outflag ] = armlet;
			}
		}

	} // end search of armlets

}

void COptimiser::setCodeCache(CCodeCache *aCodeCache)
{
	generator->setCodeCache(aCodeCache);
}
