mirror of
https://github.com/cc65/cc65.git
synced 2024-05-28 00:48:27 +00:00
470 lines
20 KiB
C
470 lines
20 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* 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
|