/* cc65 */
#include "codeent.h"
#include "codeseg.h"
+#include "datatype.h"
#include "error.h"
+#include "symtab.h"
#include "codeinfo.h"
* load all registers.
*/
{
- /* Search for the function */
- const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount,
- sizeof(FuncInfo), CompareFuncInfo);
-
- /* Do we know the function? */
- if (Info) {
- /* Use the information we have */
- *Use = Info->Use;
- *Chg = Info->Chg;
+ /* If the function name starts with an underline, it is an external
+ * function. Search for it in the symbol table. If the function does
+ * not start with an underline, it may be a runtime support function.
+ * Search for it in the list of builtin functions.
+ */
+ if (Name[0] == '_') {
+
+ /* Search in the symbol table, skip the leading underscore */
+ SymEntry* E = FindSym (Name+1);
+
+ /* Did we find it in the top level table? */
+ if (E && E->Owner->PrevTab == 0 && IsTypeFunc (E->Type)) {
+
+ /* A function may use the A or A/X registers if it is a fastcall
+ * function. Otherwise it does not use any registers passed by
+ * the caller. However, we assume that any function will destroy
+ * all registers.
+ */
+ FuncDesc* D = E->V.F.Func;
+ if ((D->Flags & FD_FASTCALL) != 0 && D->ParamCount > 0) {
+ /* Will use registers depending on the last param */
+ SymEntry* LastParam = D->SymTab->SymTail;
+ if (SizeOf (LastParam->Type) == 1) {
+ *Use = REG_A;
+ } else {
+ *Use = REG_AX;
+ }
+ } else {
+ /* Will not use any registers */
+ *Use = REG_NONE;
+ }
+
+ /* Will destroy all registers */
+ *Chg = REG_AXY;
+
+ /* Done */
+ return;
+ }
+
} else {
- /* Assume all registers used */
- *Use = REG_AXY;
- *Chg = REG_AXY;
+
+ /* Search for the function in the list of builtin functions */
+ const FuncInfo* Info = bsearch (Name, FuncInfoTable, FuncInfoCount,
+ sizeof(FuncInfo), CompareFuncInfo);
+
+ /* Do we know the function? */
+ if (Info) {
+ /* Use the information we have */
+ *Use = Info->Use;
+ *Chg = Info->Chg;
+ return;
+ }
}
+
+ /* Function not found - assume all registers used */
+ *Use = REG_AXY;
+ *Chg = REG_AXY;
}
L[3]->OPC == OPC_DEY &&
!CodeEntryHasLabel (L[3]) &&
IsSpLoad (L[4]) &&
- !CodeEntryHasLabel (L[4]));
+ !CodeEntryHasLabel (L[4]));
}
+/*****************************************************************************/
+/* Optimize subtractions */
+/*****************************************************************************/
+
+
+
+static unsigned OptSub1 (CodeSeg* S)
+/* Search for the sequence
+ *
+ * sbc ...
+ * bcs L
+ * dex
+ * L:
+ *
+ * and remove the handling of the high byte if X is not used later.
+ */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < GetCodeEntryCount (S)) {
+
+ CodeEntry* L[3];
+
+ /* Get next entry */
+ CodeEntry* E = GetCodeEntry (S, I);
+
+ /* Check for the sequence */
+ if (E->OPC == OPC_SBC &&
+ GetCodeEntries (S, L, I+1, 3) &&
+ (L[0]->OPC == OPC_BCS || L[0]->OPC == OPC_JCS) &&
+ L[0]->JumpTo != 0 &&
+ !CodeEntryHasLabel (L[0]) &&
+ L[1]->OPC == OPC_DEX &&
+ !CodeEntryHasLabel (L[1]) &&
+ L[0]->JumpTo->Owner == L[2] &&
+ !RegXUsed (S, I+3)) {
+
+ /* Remove the bcs/dex */
+ DelCodeEntries (S, I+1, 2);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+static unsigned OptSub2 (CodeSeg* S)
+/* Search for the sequence
+ *
+ * lda xx
+ * sec
+ * sta tmp1
+ * lda yy
+ * sbc tmp1
+ * sta yy
+ *
+ * and replace it by
+ *
+ * sec
+ * lda yy
+ * sbc xx
+ * sta yy
+ */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < GetCodeEntryCount (S)) {
+
+ CodeEntry* L[5];
+
+ /* Get next entry */
+ CodeEntry* E = GetCodeEntry (S, I);
+
+ /* Check for the sequence */
+ if (E->OPC == OPC_LDA &&
+ GetCodeEntries (S, L, I+1, 5) &&
+ L[0]->OPC == OPC_SEC &&
+ !CodeEntryHasLabel (L[0]) &&
+ L[1]->OPC == OPC_STA &&
+ strcmp (L[1]->Arg, "tmp1") == 0 &&
+ !CodeEntryHasLabel (L[1]) &&
+ L[2]->OPC == OPC_LDA &&
+ !CodeEntryHasLabel (L[2]) &&
+ L[3]->OPC == OPC_SBC &&
+ strcmp (L[3]->Arg, "tmp1") == 0 &&
+ !CodeEntryHasLabel (L[3]) &&
+ L[4]->OPC == OPC_STA &&
+ strcmp (L[4]->Arg, L[2]->Arg) == 0 &&
+ !CodeEntryHasLabel (L[4])) {
+
+ /* Remove the store to tmp1 */
+ DelCodeEntry (S, I+2);
+
+ /* Remove the subtraction */
+ DelCodeEntry (S, I+3);
+
+ /* Move the lda to the position of the subtraction and change the
+ * op to SBC.
+ */
+ MoveCodeEntry (S, I, I+3);
+ ReplaceOPC (E, OPC_SBC);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
+/*****************************************************************************/
+/* Optimize additions */
+/*****************************************************************************/
+
+
+
+static unsigned OptAdd1 (CodeSeg* S)
+/* Search for the sequence
+ *
+ * adc ...
+ * bcc L
+ * inx
+ * L:
+ *
+ * and remove the handling of the high byte if X is not used later.
+ */
+{
+ unsigned Changes = 0;
+
+ /* Walk over the entries */
+ unsigned I = 0;
+ while (I < GetCodeEntryCount (S)) {
+
+ CodeEntry* L[3];
+
+ /* Get next entry */
+ CodeEntry* E = GetCodeEntry (S, I);
+
+ /* Check for the sequence */
+ if (E->OPC == OPC_ADC &&
+ GetCodeEntries (S, L, I+1, 3) &&
+ (L[0]->OPC == OPC_BCC || L[0]->OPC == OPC_JCC) &&
+ L[0]->JumpTo != 0 &&
+ !CodeEntryHasLabel (L[0]) &&
+ L[1]->OPC == OPC_INX &&
+ !CodeEntryHasLabel (L[1]) &&
+ L[0]->JumpTo->Owner == L[2] &&
+ !RegXUsed (S, I+3)) {
+
+ /* Remove the bcs/dex */
+ DelCodeEntries (S, I+1, 2);
+
+ /* Remember, we had changes */
+ ++Changes;
+
+ }
+
+ /* Next entry */
+ ++I;
+
+ }
+
+ /* Return the number of changes made */
+ return Changes;
+}
+
+
+
/*****************************************************************************/
/* Optimizations for compares */
/*****************************************************************************/
/* Table with optimizer steps - are called in this order */
static OptFunc OptFuncs [] = {
+ /* Optimize subtractions */
+ { OptSub1, "OptSub1", 0 },
+ { OptSub2, "OptSub2", 0 },
+ /* Optimize additions */
+ { OptAdd1, "OptAdd1", 0 },
/* Optimize jump cascades */
{ OptJumpCascades, "OptJumpCascades", 0 },
/* Remove dead jumps */
{ OptBoolTransforms, "OptBoolTransforms", 0 },
/* Optimize calls to nega */
{ OptNegA1, "OptNegA1", 0 },
- /* Optimize calls to nega */
{ OptNegA2, "OptNegA2", 0 },
/* Optimize calls to negax */
{ OptNegAX1, "OptNegAX1", 0 },
- /* Optimize calls to negax */
{ OptNegAX2, "OptNegAX2", 0 },
- /* Optimize calls to negax */
{ OptNegAX3, "OptNegAX3", 0 },
/* Optimize compares */
{ OptCmp1, "OptCmp1", 0 },
- /* Optimize compares */
{ OptCmp2, "OptCmp2", 0 },
- /* Optimize compares */
{ OptCmp3, "OptCmp3", 0 },
- /* Optimize compares */
{ OptCmp4, "OptCmp4", 0 },
/* Remove unused loads */
{ OptUnusedLoads, "OptUnusedLoads", 0 },