/*****************************************************************************/
/*                                                                           */
/*                               codeoptutil.h                               */
/*                                                                           */
/*           Optimize operations that take operands via the stack            */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 2001      Ullrich von Bassewitz                                       */
/*               Wacholderweg 14                                             */
/*               D-70597 Stuttgart                                           */
/* EMail:        uz@cc65.org                                                 */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#ifndef CODEOPTUTIL_H
#define CODEOPTUTIL_H



/* cc65 */
#include "codeent.h"
#include "codeseg.h"



/*****************************************************************************/
/*                            Load tracking data                             */
/*****************************************************************************/



/* LoadRegInfo flags set by DirectOp */
typedef enum {
    LI_NONE         = 0x00,
    LI_DIRECT       = 0x01,         /* Direct op may be used */
    LI_RELOAD_Y     = 0x02,         /* Reload index register Y */
    LI_REMOVE       = 0x04,         /* Load may be removed */
    LI_DONT_REMOVE  = 0x08,         /* Load may not be removed */
    LI_CHECK_ARG    = 0x10,         /* Load src might be modified later */
    LI_CHECK_Y      = 0x20,         /* Indexed load src might be modified later */
    LI_SRC_USE      = 0x40,         /* src of Opc argument is possibly used */
    LI_SRC_CHG      = 0x80,         /* src of Opc argument is possibly modified */
    LI_Y_SRC_USE    = 0x0100,       /* src of Opc addressing Y is possibly used */
    LI_Y_SRC_CHG    = 0x0200,       /* src of Opc addressing Y is possibly modified */
    LI_Y_USE        = 0x0400,       /* Opc addressing Y is possibly used */
    LI_Y_CHG        = 0x0800,       /* Opc addressing Y is possibly modified */
    LI_USED_BY_A    = 0x1000,       /* Content used by RegA */
    LI_USED_BY_X    = 0x2000,       /* Content used by RegX */
    LI_USED_BY_Y    = 0x4000,       /* Content used by RegY */
    LI_SP           = 0x8000,       /* Content on stack */
    LI_LOAD_INSN    = 0x010000,     /* Is a load insn */
} LI_FLAGS;

/* Structure that tells us how to load the lhs values */
typedef struct LoadRegInfo LoadRegInfo;
struct LoadRegInfo {
    LI_FLAGS        Flags;          /* Tells us how to load */
    int             LoadIndex;      /* Index of load insn, -1 if invalid */
    CodeEntry*      LoadEntry;      /* The actual entry, 0 if invalid */
    int             LoadYIndex;     /* Index of Y-load insn, -1 if invalid  */
    CodeEntry*      LoadYEntry;     /* The actual Y-load entry, 0 if invalid */
    int             ChgIndex;       /* Index of last change */
    CodeEntry*      ChgEntry;       /* The actual change entry */
    int             Offs;           /* Stack offset if data is on stack */
};

/* Now combined for both registers */
typedef struct LoadInfo LoadInfo;
struct LoadInfo {
    LoadRegInfo     A;              /* Info for A register */
    LoadRegInfo     X;              /* Info for X register */
    LoadRegInfo     Y;              /* Info for Y register */
};

/* Structure forward decl */
typedef struct StackOpData StackOpData;

/* Structure that holds the needed data */
struct StackOpData {
    CodeSeg*    Code;           /* Pointer to code segment */
    unsigned    Flags;          /* Flags to remember things */

    /* Pointer to optimizer subfunction description */
    const void* OptFunc;

    /* ZP register usage inside the sequence */
    unsigned    ZPUsage;
    unsigned    ZPChanged;

    /* Freedom of registers inside the sequence */
    unsigned    UsedRegs;       /* Registers used */

    /* Whether the rhs is changed multiple times */
    int         RhsMultiChg;

    /* Register load information for lhs, rhs and rv */
    LoadInfo    Lhs;
    LoadInfo    Rhs;
    LoadInfo    Rv;

    /* Several indices of insns in the code segment */
    int         PushIndex;      /* Index of call to pushax in codeseg */
    int         OpIndex;        /* Index of actual operation */

    /* Pointers to insns in the code segment */
    CodeEntry*  PrevEntry;      /* Entry before the call to pushax */
    CodeEntry*  PushEntry;      /* Pointer to entry with call to pushax */
    CodeEntry*  OpEntry;        /* Pointer to entry with op */
    CodeEntry*  NextEntry;      /* Entry after the op */

    const char* ZPLo;           /* Lo byte of zero page loc to use */
    const char* ZPHi;           /* Hi byte of zero page loc to use */
    unsigned    IP;             /* Insertion point used by some routines */
};



/*****************************************************************************/
/*                            Load tracking code                             */
/*****************************************************************************/



void ClearLoadRegInfo (LoadRegInfo* LRI);
/* Clear a LoadRegInfo struct */

void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From);
/* Copy a LoadRegInfo struct */

void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S);
/* Prepare a LoadRegInfo struct for use */

void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change);
/* Adjust a load register info struct after deleting or inserting an entry
** with a given index
*/

void ClearLoadInfo (LoadInfo* LI);
/* Clear a LoadInfo struct */

void CopyLoadInfo (LoadInfo* To, LoadInfo* From);
/* Copy a LoadInfo struct */

void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S);
/* Prepare a LoadInfo struct for use */

void AdjustLoadInfo (LoadInfo* LI, int Index, int Change);
/* Adjust a load info struct after deleting entry with a given index */

RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg);
/* Get RegInfo of the last insn entry that changed the reg */

void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E);
/* Set the load src flags and remember to check for load src change if necessary */

void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E);
/* Check and flag operand src that may be affected */

void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used);
/* Check and flag operand load that may be unremovable */

unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I);
/* Track loads for a code entry.
** Return used registers.
*/

void SetDontRemoveEntryFlag (LoadRegInfo* LRI);
/* Flag the entry as non-removable according to register flags */

void ResetDontRemoveEntryFlag (LoadRegInfo* LRI);
/* Unflag the entry as non-removable according to register flags */

void SetDontRemoveEntryFlags (StackOpData* D);
/* Flag the entries as non-removable according to register flags */

void ResetDontRemoveEntryFlags (StackOpData* D);
/* Unflag the entries as non-removable according to register flags */

void ResetStackOpData (StackOpData* Data);
/* Reset the given data structure */



/*****************************************************************************/
/*                                  Helpers                                  */
/*****************************************************************************/



void InsertEntry (StackOpData* D, CodeEntry* E, int Index);
/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will
** be adjusted by this function.
*/

void DelEntry (StackOpData* D, int Index);
/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be
** adjusted by this function, and PushEntry/OpEntry may get invalidated.
*/

void AdjustStackOffset (StackOpData* D, unsigned Offs);
/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex.
** OpIndex is adjusted according to the insertions.
*/

int IsRegVar (StackOpData* D);
/* If the value pushed is that of a zeropage variable that is unchanged until Op,
** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true.
** Otherwise leave D untouched and return false.
*/

void AddStoreLhsA (StackOpData* D);
/* Add a store to zero page after the push insn */

void AddStoreLhsX (StackOpData* D);
/* Add a store to zero page after the push insn */

void ReplacePushByStore (StackOpData* D);
/* Replace the call to the push subroutine by a store into the zero page
** location (actually, the push is not replaced, because we need it for
** later, but the name is still ok since the push will get removed at the
** end of each routine).
*/

void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI);
/* Add an op for the low byte of an operator. This function honours the
** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
** All code is inserted at the current insertion point.
*/

void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult);
/* Add an op for the high byte of an operator. Special cases (constant values
** or similar) have to be checked separately, the function covers only the
** generic case. Code is inserted at the insertion point.
*/

void RemoveRegLoads (StackOpData* D, LoadInfo* LI);
/* Remove register load insns */


void RemoveRemainders (StackOpData* D);
/* Remove the code that is unnecessary after translation of the sequence */

int HarmlessCall (const CodeEntry* E, int PushedBytes);
/* Check if this is a call to a harmless subroutine that will not interrupt
** the pushax/op sequence when encountered.
*/



/*****************************************************************************/
/*                                  Helpers                                  */
/*****************************************************************************/



/* Backup location types */
#define BU_UNKNOWN      0x00000000U   /* Unknown */
#define BU_IMM          0x00000000U   /* Immediate */
#define BU_REG          0x01000000U   /* In register */
#define BU_ZP           0x02000000U   /* On ZP */
#define BU_SP6502       0x04000000U   /* On 6502 stack */
#define BU_SP           0x08000000U   /* On CC65 stack */
#define BU_B8           0x00000000U   /* Size of 8-bit */
#define BU_B16          0x10000000U   /* Size of 16-bit */
#define BU_B24          0x20000000U   /* Size of 24-bit */
#define BU_B32          0x30000000U   /* Size of 32-bit */
#define BU_TYPE_MASK    0x0F000000U   /* Type mask */
#define BU_SIZE_MASK    0xF0000000U   /* Size mask */

typedef struct {
    unsigned Type;              /* Backup location type and size */
    unsigned ZPUsage;           /* ZP unusable for backup */
    union {
        unsigned        Where;  /* Backup location */
        unsigned        Imm;    /* Backed-up value */
        unsigned char*  Bytes;  /* Pointer to backed-up value */
    };
} BackupInfo;



const char* GetZPName (unsigned ZPLoc);
/* Get the name strings of certain known ZP Regs */

unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type);
/* Find a ZP loc for storing the backup and fill in the info.
** The allowed types are specified with the Type parameter.
** For convenience, all types are aloowed if none is specified.
** Return the type of the found loc.
*/

void AdjustEntryIndices (Collection* Indices, int Index, int Change);
/* Adjust a load register info struct after deleting or inserting successive
** entries with a given index.
*/

void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices);
/* Delete an entry and adjust Indices if necessary */

void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices);
/* Delete entries and adjust Indices if necessary */

void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices);
/* Remove flagged register load insns */

void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices);
/* Remove flagged load insns */

int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of A before the specified index Idx */

int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of X before the specified index Idx */

int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of Y before the specified index Idx */

int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of AX before the specified index Idx */

int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of AXY before the specified index Idx.
** This doesn't allow separating the backup of Y from that of AX for now.
*/

int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of A after the specified index Idx */

int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of X after the specified index Idx */

int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of Y after the specified index Idx */

int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of AX after the specified index Idx */

int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Backup the content of AXY after the specified index Idx.
** This doesn't allow separating the backup of Y from that of AX for now.
*/

int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Restore the content of Y before the specified index Idx */

int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Restore the content of X before the specified index Idx */

int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Restore the content of Y before the specified index Idx */

int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Restore the content of AX before the specified index Idx */

int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices);
/* Restore the content of AXY before the specified index Idx.
** This only allows restore from compacted AXY backup for now.
*/

int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices);
/* Backup the content of the opc arg of the entry E after the specified index Idx.
** Reg A/Y will be used to transfer the content from a memory location to another
** regardless of whether it is in use.
*/

int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into A the same arg according to LoadRegInfo at Idx */

int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into X the same arg according to LoadRegInfo at Idx */

int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into Y the same arg according to LoadRegInfo at Idx */

int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into A the same arg according to LoadRegInfo after Idx */

int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into X the same arg according to LoadRegInfo after Idx */

int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices);
/* Reload into Y the same arg according to LoadRegInfo after Idx */

unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last);
/* Get what ZPs, registers or processor states are used or changed in the range
** (First, Last).
** The code block must be basic without any jump backwards.
*/

unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg);
/* Get what ZPs, registers or processor states are used or changed in the range
** (First, Last) in output parameters Use and Chg.
** Return what ZP regs are used before changed in this range.
** The code block must be basic without any jump backwards.
*/

int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E);
/* Check if the loading the opc arg gives the same result everywhere between (First, Last).
** The code block in the range must be basic without any jump backwards.
** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set.
*/

int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E);
/* Find the first possible spot where the loaded arg of E might be changed in
** the range (First, Last). The code block in the range must be basic without
** any jump backwards.
** Return the index of the found entry, or Last if not found.
** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set.
*/

int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY);
/* Find the last index where the arg of E might be used or changed in the range (First, Last).
** ReloadY indicates whether Y is supposed to be reloaded.
** The code block in the range must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/

int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
/* Find the first possible spot where the queried ZPs, registers and/or processor
** states might be changed in the range (First, Last). The code block in the
** range must be basic without any jump backwards.
** Return the index of the found entry, or Last if not found.
*/

int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
/* Find the first possible spot where the queried ZPs, registers and/or processor
** states might be used in the range (First, Last). The code block in the range
** must be basic without any jump backwards.
** Return the index of the found entry, or Last if not found.
*/

int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
/* Find the last possible spot where the queried ZPs, registers and/or processor
** states might be changed in the range (First, Last). The code block in the
** range must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/

int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
/* Find the last possible spot where the queried ZPs, registers and/or processor
** states might be used in the range (First, Last). The code block in the range
** must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/

/* End of codeoptutil.h */

#endif
