Change code generated for compares for special cases.
git-svn-id: svn://svn.cc65.org/cc65/trunk@4062 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
@@ -3486,6 +3486,8 @@ void g_lt (unsigned flags, unsigned long val)
|
|||||||
"tosltax", "tosultax", "toslteax", "tosulteax",
|
"tosltax", "tosultax", "toslteax", "tosulteax",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsigned Label;
|
||||||
|
|
||||||
/* If the right hand side is const, the lhs is not on stack but still
|
/* If the right hand side is const, the lhs is not on stack but still
|
||||||
* in the primary register.
|
* in the primary register.
|
||||||
*/
|
*/
|
||||||
@@ -3545,32 +3547,84 @@ void g_lt (unsigned flags, unsigned long val)
|
|||||||
|
|
||||||
} else if (val == 0) {
|
} else if (val == 0) {
|
||||||
|
|
||||||
/* Look at the type */
|
/* A signed compare against zero must only look at the sign bit */
|
||||||
switch (flags & CF_TYPE) {
|
switch (flags & CF_TYPE) {
|
||||||
|
|
||||||
case CF_CHAR:
|
case CF_CHAR:
|
||||||
if (flags & CF_FORCECHAR) {
|
if (flags & CF_FORCECHAR) {
|
||||||
AddCodeLine ("tax");
|
AddCodeLine ("asl a"); /* Bit 7 -> carry */
|
||||||
AddCodeLine ("jsr boollt");
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
case CF_INT:
|
case CF_INT:
|
||||||
/* Just check the high byte */
|
/* Just check the high byte */
|
||||||
AddCodeLine ("txa");
|
AddCodeLine ("cpx #$80"); /* Bit 7 -> carry */
|
||||||
AddCodeLine ("jsr boollt");
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case CF_LONG:
|
case CF_LONG:
|
||||||
/* Just check the high byte */
|
/* Just check the high byte */
|
||||||
AddCodeLine ("lda sreg+1");
|
AddCodeLine ("lda sreg+1");
|
||||||
AddCodeLine ("jsr boollt");
|
AddCodeLine ("asl a"); /* Bit 7 -> carry */
|
||||||
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
typeerror (flags);
|
typeerror (flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Signed compare against a constant != zero */
|
||||||
|
switch (flags & CF_TYPE) {
|
||||||
|
|
||||||
|
case CF_CHAR:
|
||||||
|
if (flags & CF_FORCECHAR) {
|
||||||
|
Label = GetLocalLabel ();
|
||||||
|
AddCodeLine ("sec");
|
||||||
|
AddCodeLine ("sbc #$%02X", (unsigned char)val);
|
||||||
|
AddCodeLine ("bvc %s", LocalLabelName (Label));
|
||||||
|
AddCodeLine ("eor #$80");
|
||||||
|
g_defcodelabel (Label);
|
||||||
|
AddCodeLine ("asl a"); /* Bit 7 -> carry */
|
||||||
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
case CF_INT:
|
||||||
|
/* Do a subtraction */
|
||||||
|
Label = GetLocalLabel ();
|
||||||
|
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||||
|
AddCodeLine ("txa");
|
||||||
|
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
|
||||||
|
AddCodeLine ("bvc %s", LocalLabelName (Label));
|
||||||
|
AddCodeLine ("eor #$80");
|
||||||
|
g_defcodelabel (Label);
|
||||||
|
AddCodeLine ("asl a"); /* Bit 7 -> carry */
|
||||||
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CF_LONG:
|
||||||
|
/* This one is too costly */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
typeerror (flags);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we go here, we didn't emit code. Push the lhs on stack and fall
|
/* If we go here, we didn't emit code. Push the lhs on stack and fall
|
||||||
@@ -3864,26 +3918,27 @@ void g_ge (unsigned flags, unsigned long val)
|
|||||||
|
|
||||||
case CF_CHAR:
|
case CF_CHAR:
|
||||||
if (flags & CF_FORCECHAR) {
|
if (flags & CF_FORCECHAR) {
|
||||||
|
/* Do a subtraction. Condition is true if carry set */
|
||||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||||
AddCodeLine ("jsr booluge");
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
case CF_INT:
|
case CF_INT:
|
||||||
/* If the low byte is zero, we must only test the high byte */
|
/* Do a subtraction. Condition is true if carry set */
|
||||||
AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
|
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||||
if ((val & 0xFF) != 0) {
|
AddCodeLine ("txa");
|
||||||
unsigned L = GetLocalLabel();
|
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
|
||||||
AddCodeLine ("bne %s", LocalLabelName (L));
|
AddCodeLine ("lda #$00");
|
||||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
AddCodeLine ("ldx #$00");
|
||||||
g_defcodelabel (L);
|
AddCodeLine ("rol a");
|
||||||
}
|
|
||||||
AddCodeLine ("jsr booluge");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case CF_LONG:
|
case CF_LONG:
|
||||||
/* Do a subtraction */
|
/* Do a subtraction. Condition is true if carry set */
|
||||||
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
AddCodeLine ("cmp #$%02X", (unsigned char)val);
|
||||||
AddCodeLine ("txa");
|
AddCodeLine ("txa");
|
||||||
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
|
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
|
||||||
@@ -3891,7 +3946,9 @@ void g_ge (unsigned flags, unsigned long val)
|
|||||||
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 16));
|
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 16));
|
||||||
AddCodeLine ("lda sreg+1");
|
AddCodeLine ("lda sreg+1");
|
||||||
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 24));
|
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 24));
|
||||||
AddCodeLine ("jsr booluge");
|
AddCodeLine ("lda #$00");
|
||||||
|
AddCodeLine ("ldx #$00");
|
||||||
|
AddCodeLine ("rol a");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -3900,7 +3957,7 @@ void g_ge (unsigned flags, unsigned long val)
|
|||||||
|
|
||||||
} else if (val == 0) {
|
} else if (val == 0) {
|
||||||
|
|
||||||
/* Look at the type */
|
/* A signed compare against zero must only look at the sign bit */
|
||||||
switch (flags & CF_TYPE) {
|
switch (flags & CF_TYPE) {
|
||||||
|
|
||||||
case CF_CHAR:
|
case CF_CHAR:
|
||||||
|
|||||||
@@ -408,11 +408,11 @@ static unsigned OptShift4 (CodeSeg* S)
|
|||||||
* ldx tmp1
|
* ldx tmp1
|
||||||
*
|
*
|
||||||
* which makes 4 + 3 * ShiftCount bytes, compared to the original
|
* which makes 4 + 3 * ShiftCount bytes, compared to the original
|
||||||
* 3 bytes for the subroutine call. However, in most cases, the
|
* 3 bytes for the subroutine call. However, in most cases, the
|
||||||
* final load of the X register gets merged with some other insn
|
* final load of the X register gets merged with some other insn
|
||||||
* and replaces a txa, so for a shift count of 1, we get a factor
|
* and replaces a txa, so for a shift count of 1, we get a factor
|
||||||
* of 200, which matches nicely the CodeSizeFactor enabled with -Oi
|
* of 200, which matches nicely the CodeSizeFactor enabled with -Oi
|
||||||
*/
|
*/
|
||||||
if (ShiftCount > 1 || S->CodeSizeFactor > 200) {
|
if (ShiftCount > 1 || S->CodeSizeFactor > 200) {
|
||||||
unsigned Size = 4 + 3 * ShiftCount;
|
unsigned Size = 4 + 3 * ShiftCount;
|
||||||
if ((Size * 100 / 3) > S->CodeSizeFactor) {
|
if ((Size * 100 / 3) > S->CodeSizeFactor) {
|
||||||
@@ -1097,7 +1097,8 @@ static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 0,
|
|||||||
static OptFunc DOptCmp6 = { OptCmp6, "OptCmp6", 100, 0, 0, 0, 0, 0 };
|
static OptFunc DOptCmp6 = { OptCmp6, "OptCmp6", 100, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, 0, 0, 0, 0 };
|
static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 };
|
static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptCondBranches = { OptCondBranches, "OptCondBranches", 80, 0, 0, 0, 0, 0 };
|
static OptFunc DOptCondBranches1= { OptCondBranches1,"OptCondBranches1", 80, 0, 0, 0, 0, 0 };
|
||||||
|
static OptFunc DOptCondBranches2= { OptCondBranches2,"OptCondBranches2", 0, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 0, 0, 0, 0 };
|
static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 };
|
static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 };
|
||||||
static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 };
|
static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 };
|
||||||
@@ -1181,7 +1182,8 @@ static OptFunc* OptFuncs[] = {
|
|||||||
&DOptCmp6,
|
&DOptCmp6,
|
||||||
&DOptCmp7,
|
&DOptCmp7,
|
||||||
&DOptCmp8,
|
&DOptCmp8,
|
||||||
&DOptCondBranches,
|
&DOptCondBranches1,
|
||||||
|
&DOptCondBranches2,
|
||||||
&DOptDeadCode,
|
&DOptDeadCode,
|
||||||
&DOptDeadJumps,
|
&DOptDeadJumps,
|
||||||
&DOptDecouple,
|
&DOptDecouple,
|
||||||
@@ -1558,7 +1560,8 @@ static unsigned RunOptGroup3 (CodeSeg* S)
|
|||||||
C += RunOptFunc (S, &DOptDeadCode, 1);
|
C += RunOptFunc (S, &DOptDeadCode, 1);
|
||||||
C += RunOptFunc (S, &DOptJumpTarget1, 1);
|
C += RunOptFunc (S, &DOptJumpTarget1, 1);
|
||||||
C += RunOptFunc (S, &DOptJumpTarget2, 1);
|
C += RunOptFunc (S, &DOptJumpTarget2, 1);
|
||||||
C += RunOptFunc (S, &DOptCondBranches, 1);
|
C += RunOptFunc (S, &DOptCondBranches1, 1);
|
||||||
|
C += RunOptFunc (S, &DOptCondBranches2, 1);
|
||||||
C += RunOptFunc (S, &DOptRTSJumps1, 1);
|
C += RunOptFunc (S, &DOptRTSJumps1, 1);
|
||||||
C += RunOptFunc (S, &DOptBoolTrans, 1);
|
C += RunOptFunc (S, &DOptBoolTrans, 1);
|
||||||
C += RunOptFunc (S, &DOptCmp1, 1);
|
C += RunOptFunc (S, &DOptCmp1, 1);
|
||||||
|
|||||||
@@ -706,7 +706,7 @@ NextEntry:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned OptCondBranches (CodeSeg* S)
|
unsigned OptCondBranches1 (CodeSeg* S)
|
||||||
/* Performs several optimization steps:
|
/* Performs several optimization steps:
|
||||||
*
|
*
|
||||||
* - If an immidiate load of a register is followed by a conditional jump that
|
* - If an immidiate load of a register is followed by a conditional jump that
|
||||||
@@ -799,6 +799,63 @@ unsigned OptCondBranches (CodeSeg* S)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unsigned OptCondBranches2 (CodeSeg* S)
|
||||||
|
/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows,
|
||||||
|
* we can remove the rol and branch on the state of the carry.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned Changes = 0;
|
||||||
|
|
||||||
|
/* Generate register info for this step */
|
||||||
|
CS_GenRegInfo (S);
|
||||||
|
|
||||||
|
/* Walk over the entries */
|
||||||
|
unsigned I = 0;
|
||||||
|
while (I < CS_GetEntryCount (S)) {
|
||||||
|
|
||||||
|
CodeEntry* N;
|
||||||
|
|
||||||
|
/* Get next entry */
|
||||||
|
CodeEntry* E = CS_GetEntry (S, I);
|
||||||
|
|
||||||
|
/* Check if it's a rol insn with A in accu and a branch follows */
|
||||||
|
if (E->OPC == OP65_ROL &&
|
||||||
|
E->AM == AM65_ACC &&
|
||||||
|
E->RI->In.RegA == 0 &&
|
||||||
|
!CE_HasLabel (E) &&
|
||||||
|
(N = CS_GetNextEntry (S, I)) != 0 &&
|
||||||
|
(N->Info & OF_ZBRA) != 0 &&
|
||||||
|
!RegAUsed (S, I+1)) {
|
||||||
|
|
||||||
|
/* Replace the branch condition */
|
||||||
|
switch (GetBranchCond (N->OPC)) {
|
||||||
|
case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break;
|
||||||
|
case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break;
|
||||||
|
default: Internal ("Unknown branch condition in OptCondBranches2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete the rol insn */
|
||||||
|
CS_DelEntry (S, I);
|
||||||
|
|
||||||
|
/* Remember, we had changes */
|
||||||
|
++Changes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next entry */
|
||||||
|
++I;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free register info */
|
||||||
|
CS_FreeRegInfo (S);
|
||||||
|
|
||||||
|
/* Return the number of changes made */
|
||||||
|
return Changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Remove unused loads and stores */
|
/* Remove unused loads and stores */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -975,7 +1032,7 @@ unsigned OptDupLoads (CodeSeg* S)
|
|||||||
* remove the store.
|
* remove the store.
|
||||||
*/
|
*/
|
||||||
if (RegValIsKnown (In->RegA) && /* Value of A is known */
|
if (RegValIsKnown (In->RegA) && /* Value of A is known */
|
||||||
E->AM == AM65_ZP && /* Store into zp */
|
E->AM == AM65_ZP && /* Store into zp */
|
||||||
In->RegA == ZPRegVal (E->Chg, In)) { /* Value identical */
|
In->RegA == ZPRegVal (E->Chg, In)) { /* Value identical */
|
||||||
|
|
||||||
Delete = 1;
|
Delete = 1;
|
||||||
@@ -1028,7 +1085,7 @@ unsigned OptDupLoads (CodeSeg* S)
|
|||||||
*/
|
*/
|
||||||
} else if (RegValIsKnown (In->RegY)) {
|
} else if (RegValIsKnown (In->RegY)) {
|
||||||
if (In->RegY == In->RegA) {
|
if (In->RegY == In->RegA) {
|
||||||
CE_ReplaceOPC (E, OP65_STA);
|
CE_ReplaceOPC (E, OP65_STA);
|
||||||
} else if (In->RegY == In->RegX &&
|
} else if (In->RegY == In->RegX &&
|
||||||
E->AM != AM65_ABSX &&
|
E->AM != AM65_ABSX &&
|
||||||
E->AM != AM65_ZPX) {
|
E->AM != AM65_ZPX) {
|
||||||
@@ -1081,7 +1138,7 @@ unsigned OptDupLoads (CodeSeg* S)
|
|||||||
|
|
||||||
case OP65_TYA:
|
case OP65_TYA:
|
||||||
if (RegValIsKnown (In->RegY) &&
|
if (RegValIsKnown (In->RegY) &&
|
||||||
In->RegY == In->RegA &&
|
In->RegY == In->RegA &&
|
||||||
(N = CS_GetNextEntry (S, I)) != 0 &&
|
(N = CS_GetNextEntry (S, I)) != 0 &&
|
||||||
!CE_UseLoadFlags (N)) {
|
!CE_UseLoadFlags (N)) {
|
||||||
/* Value is identical and not followed by a branch */
|
/* Value is identical and not followed by a branch */
|
||||||
@@ -1130,10 +1187,10 @@ unsigned OptStoreLoad (CodeSeg* S)
|
|||||||
unsigned I = 0;
|
unsigned I = 0;
|
||||||
while (I < CS_GetEntryCount (S)) {
|
while (I < CS_GetEntryCount (S)) {
|
||||||
|
|
||||||
CodeEntry* N;
|
CodeEntry* N;
|
||||||
CodeEntry* X;
|
CodeEntry* X;
|
||||||
|
|
||||||
/* Get next entry */
|
/* Get next entry */
|
||||||
CodeEntry* E = CS_GetEntry (S, I);
|
CodeEntry* E = CS_GetEntry (S, I);
|
||||||
|
|
||||||
/* Check if it is a store instruction followed by a load from the
|
/* Check if it is a store instruction followed by a load from the
|
||||||
@@ -1887,7 +1944,7 @@ unsigned OptPrecalc (CodeSeg* S)
|
|||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned OptBranchDist (CodeSeg* S)
|
unsigned OptBranchDist (CodeSeg* S)
|
||||||
/* Change branches for the distance needed. */
|
/* Change branches for the distance needed. */
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,12 +89,17 @@ unsigned OptJumpTarget2 (CodeSeg* S);
|
|||||||
* it's job is already done.
|
* it's job is already done.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unsigned OptCondBranches (CodeSeg* S);
|
unsigned OptCondBranches1 (CodeSeg* S);
|
||||||
/* If an immidiate load of a register is followed by a conditional jump that
|
/* If an immidiate load of a register is followed by a conditional jump that
|
||||||
* is never taken because the load of the register sets the flags in such a
|
* is never taken because the load of the register sets the flags in such a
|
||||||
* manner, remove the conditional branch.
|
* manner, remove the conditional branch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
unsigned OptCondBranches2 (CodeSeg* S);
|
||||||
|
/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows,
|
||||||
|
* we can remove the rol and branch on the state of the carry.
|
||||||
|
*/
|
||||||
|
|
||||||
unsigned OptUnusedLoads (CodeSeg* S);
|
unsigned OptUnusedLoads (CodeSeg* S);
|
||||||
/* Remove loads of registers where the value loaded is not used later. */
|
/* Remove loads of registers where the value loaded is not used later. */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user