Skip to content

Commit 97b6a07

Browse files
Merge branch 'cc65:master' into master
2 parents ac8fbae + a545b4f commit 97b6a07

File tree

6 files changed

+577
-68
lines changed

6 files changed

+577
-68
lines changed

src/cc65/codeoptutil.c

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,8 @@ void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI)
11231123
InsertEntry (D, X, D->IP++);
11241124

11251125
if (LI->A.LoadEntry->OPC == OP65_JSR) {
1126+
/* This should only happen in one known ldaxysp case. */
1127+
CHECK (CE_IsCallTo (LI->A.LoadEntry, "ldaxysp"));
11261128
/* opc (c_sp),y */
11271129
X = NewCodeEntry (OPC, AM65_ZP_INDY, "c_sp", 0, D->OpEntry->LI);
11281130
} else {
@@ -1133,7 +1135,7 @@ void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI)
11331135

11341136
}
11351137

1136-
/* In both cases, we can remove the load */
1138+
/* In both cases, we may try removing the load */
11371139
LI->A.Flags |= LI_REMOVE;
11381140

11391141
} else {
@@ -1188,6 +1190,8 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
11881190
InsertEntry (D, X, D->IP++);
11891191

11901192
if (LI->X.LoadEntry->OPC == OP65_JSR) {
1193+
/* This should only happen in one known ldaxysp case. */
1194+
CHECK (CE_IsCallTo (LI->X.LoadEntry, "ldaxysp"));
11911195
/* opc (c_sp),y */
11921196
X = NewCodeEntry (OPC, AM65_ZP_INDY, "c_sp", 0, D->OpEntry->LI);
11931197
} else {
@@ -1197,10 +1201,8 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
11971201
InsertEntry (D, X, D->IP++);
11981202
}
11991203

1200-
/* If this is the right hand side, we can remove the load. */
1201-
if (LI == &D->Rhs) {
1202-
LI->X.Flags |= LI_REMOVE;
1203-
}
1204+
/* In both cases, we may try removing the load */
1205+
LI->X.Flags |= LI_REMOVE;
12041206

12051207
} else {
12061208

@@ -1227,13 +1229,71 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
12271229
void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
12281230
/* Remove register load insns */
12291231
{
1230-
if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) {
1231-
if (LI->A.LoadIndex >= 0 &&
1232-
(LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
1233-
DelEntry (D, LI->A.LoadIndex);
1234-
LI->A.LoadEntry = 0;
1235-
}
1232+
int CanRemoveA;
1233+
int CanRemoveX;
1234+
1235+
/* When either A or X load insn is a call to ldaxysp runtime, the load
1236+
** affects both and must be treated as a single A/X unit. A request to
1237+
** remove the runtime call for just one (A or X) load is not valid in that
1238+
** case. Either both must be removable+removed, or the other load is
1239+
** independent, or ldaxysp runtime call must remain.
1240+
*/
1241+
CanRemoveA = (LI->A.Flags & LI_REMOVE) == LI_REMOVE &&
1242+
LI->A.LoadEntry != 0 &&
1243+
(LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0;
1244+
1245+
CanRemoveX = (LI->X.Flags & LI_REMOVE) == LI_REMOVE &&
1246+
LI->X.LoadEntry != 0 &&
1247+
(LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0;
1248+
1249+
/* The only runtime calls removable as load insns are calls to ldaxysp. */
1250+
CHECK (!CanRemoveA || LI->A.LoadEntry->OPC != OP65_JSR ||
1251+
CE_IsCallTo (LI->A.LoadEntry, "ldaxysp"));
1252+
CHECK (!CanRemoveX || LI->X.LoadEntry->OPC != OP65_JSR ||
1253+
CE_IsCallTo (LI->X.LoadEntry, "ldaxysp"));
1254+
1255+
/* When the A load insn affects X reg (e.g. ldaxysp runtime), and the X
1256+
** load cannot be removed, we cannot remove the A load either.
1257+
*/
1258+
if (CanRemoveA && (LI->A.LoadEntry->Chg & REG_X) != 0 && !CanRemoveX) {
1259+
/* A load is not removable */
1260+
LI->A.LoadEntry->Flags |= CEF_DONT_REMOVE;
1261+
CanRemoveA = 0;
1262+
}
1263+
1264+
/* When the X load insn affects A reg (e.g. ldaxysp runtime), and the A
1265+
** load cannot be removed, we cannot remove the X load either.
1266+
*/
1267+
if (CanRemoveX && (LI->X.LoadEntry->Chg & REG_A) != 0 && !CanRemoveA) {
1268+
/* X load is not removable */
1269+
LI->X.LoadEntry->Flags |= CEF_DONT_REMOVE;
1270+
CanRemoveX = 0;
1271+
}
1272+
1273+
/* A load removal demand which cannot be satisifed is a fatal condition */
1274+
if (((LI->A.Flags & LI_MUST_REMOVE) != 0 && !CanRemoveA) ||
1275+
((LI->X.Flags & LI_MUST_REMOVE) != 0 && !CanRemoveX)) {
1276+
Internal ("Cannot remove a load instruction which must be removed");
1277+
}
1278+
1279+
/* A request to remove a "change" insn (ChgIndex) when there is no
1280+
** corresponing load insn (LoadIndex) is never valid.
1281+
*/
1282+
CHECK ((LI->A.Flags & LI_REMOVE) == 0 || LI->A.ChgIndex < 0 ||
1283+
LI->A.LoadIndex >= 0);
1284+
CHECK ((LI->X.Flags & LI_REMOVE) == 0 || LI->X.ChgIndex < 0 ||
1285+
LI->X.LoadIndex >= 0);
1286+
1287+
if (CanRemoveA) {
1288+
1289+
CHECK (LI->A.LoadIndex >= 0);
1290+
DelEntry (D, LI->A.LoadIndex);
1291+
1292+
/* Only remove the Y load if it is not shared with a non-removable
1293+
** X load. A shared load will be removed here otherwise.
1294+
*/
12361295
if (LI->A.LoadYIndex >= 0 &&
1296+
(LI->A.LoadYIndex != LI->X.LoadYIndex || CanRemoveX) &&
12371297
(LI->A.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
12381298
DelEntry (D, LI->A.LoadYIndex);
12391299
}
@@ -1250,12 +1310,16 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
12501310
LI->A.LoadYEntry->Flags |= CEF_DONT_REMOVE;
12511311
}
12521312

1253-
if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) {
1254-
if (LI->X.LoadIndex >= 0 &&
1255-
(LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
1256-
DelEntry (D, LI->X.LoadIndex);
1257-
LI->X.LoadEntry = 0;
1258-
}
1313+
/* The X load may have already been removed above if it were a runtime
1314+
** call (i.e. "jsr ldaxysp"), so need to check again.
1315+
*/
1316+
if (CanRemoveX && LI->X.LoadIndex >= 0) {
1317+
1318+
DelEntry (D, LI->X.LoadIndex);
1319+
1320+
/* Only remove the Y load if it is not shared with non-removable A load.
1321+
** If it is shared and still needed, it was flaged non-removable above.
1322+
*/
12591323
if (LI->X.LoadYIndex >= 0 &&
12601324
(LI->X.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
12611325
DelEntry (D, LI->X.LoadYIndex);

src/cc65/codeoptutil.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef enum {
7070
LI_USED_BY_Y = 0x4000, /* Content used by RegY */
7171
LI_SP = 0x8000, /* Content on stack */
7272
LI_LOAD_INSN = 0x010000, /* Is a load insn */
73+
LI_MUST_REMOVE = 0x020000, /* Load must be removed */
7374
} LI_FLAGS;
7475

7576
/* Structure that tells us how to load the lhs values */

src/cc65/coptstop.c

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,9 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer
436436
X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
437437
InsertEntry (D, X, D->IP++);
438438

439-
/* Rhs load entries must be removed */
440-
D->Rhs.X.Flags |= LI_REMOVE;
441-
D->Rhs.A.Flags |= LI_REMOVE;
439+
/* Rhs load entries must be removed because Lhs must be in effect */
440+
D->Rhs.X.Flags |= (LI_REMOVE | LI_MUST_REMOVE);
441+
D->Rhs.A.Flags |= (LI_REMOVE | LI_MUST_REMOVE);
442442

443443
} else if (RhsIsRemovable (D)) {
444444

@@ -454,6 +454,10 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer
454454
/* Add operand for high byte */
455455
AddOpHigh (D, OP65_CMP, &D->Rhs, 0);
456456

457+
/* Rhs load entries must be removed because Lhs must be in effect */
458+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
459+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
460+
457461
} else {
458462

459463
/* Implement the op via a temp ZP location */
@@ -1018,9 +1022,9 @@ static unsigned Opt_tosgeax (StackOpData* D)
10181022
X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
10191023
InsertEntry (D, X, D->IP++);
10201024

1021-
/* Rhs load entries must be removed */
1022-
D->Rhs.X.Flags |= LI_REMOVE;
1023-
D->Rhs.A.Flags |= LI_REMOVE;
1025+
/* Rhs load entries must be removed because Lhs must be in effect */
1026+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1027+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
10241028

10251029
/* Remove the push and the call to the tosgeax function */
10261030
RemoveRemainders (D);
@@ -1075,9 +1079,9 @@ static unsigned Opt_tosltax (StackOpData* D)
10751079
X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
10761080
InsertEntry (D, X, D->IP++);
10771081

1078-
/* Rhs load entries must be removed */
1079-
D->Rhs.X.Flags |= LI_REMOVE;
1080-
D->Rhs.A.Flags |= LI_REMOVE;
1082+
/* Rhs load entries must be removed because Lhs must be in effect */
1083+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1084+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
10811085

10821086
/* Remove the push and the call to the tosltax function */
10831087
RemoveRemainders (D);
@@ -1158,9 +1162,9 @@ static unsigned Opt_tossubax (StackOpData* D)
11581162
/* Add code for high operand */
11591163
AddOpHigh (D, OP65_SBC, &D->Rhs, 1);
11601164

1161-
/* Rhs load entries must be removed */
1162-
D->Rhs.X.Flags |= LI_REMOVE;
1163-
D->Rhs.A.Flags |= LI_REMOVE;
1165+
/* Rhs load entries must be removed because Lhs must be in effect */
1166+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1167+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
11641168

11651169
/* Remove the push and the call to the tossubax function */
11661170
RemoveRemainders (D);
@@ -1200,9 +1204,9 @@ static unsigned Opt_tosugeax (StackOpData* D)
12001204
X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
12011205
InsertEntry (D, X, D->IP++);
12021206

1203-
/* Rhs load entries must be removed */
1204-
D->Rhs.X.Flags |= LI_REMOVE;
1205-
D->Rhs.A.Flags |= LI_REMOVE;
1207+
/* Rhs load entries must be removed because Lhs must be in effect */
1208+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1209+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
12061210

12071211
/* Remove the push and the call to the tosugeax function */
12081212
RemoveRemainders (D);
@@ -1246,9 +1250,9 @@ static unsigned Opt_tosugtax (StackOpData* D)
12461250
X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolugt", 0, D->OpEntry->LI);
12471251
InsertEntry (D, X, D->IP++);
12481252

1249-
/* Rhs load entries must be removed */
1250-
D->Rhs.X.Flags |= LI_REMOVE;
1251-
D->Rhs.A.Flags |= LI_REMOVE;
1253+
/* Rhs load entries must be removed because Lhs must be in effect */
1254+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1255+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
12521256

12531257
/* Remove the push and the call to the operator function */
12541258
RemoveRemainders (D);
@@ -1292,9 +1296,9 @@ static unsigned Opt_tosuleax (StackOpData* D)
12921296
X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolule", 0, D->OpEntry->LI);
12931297
InsertEntry (D, X, D->IP++);
12941298

1295-
/* Rhs load entries must be removed */
1296-
D->Rhs.X.Flags |= LI_REMOVE;
1297-
D->Rhs.A.Flags |= LI_REMOVE;
1299+
/* Rhs load entries must be removed because Lhs must be in effect */
1300+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1301+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
12981302

12991303
/* Remove the push and the call to the operator function */
13001304
RemoveRemainders (D);
@@ -1326,9 +1330,9 @@ static unsigned Opt_tosultax (StackOpData* D)
13261330
X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolult", 0, D->OpEntry->LI);
13271331
InsertEntry (D, X, D->IP++);
13281332

1329-
/* Rhs load entries must be removed */
1330-
D->Rhs.X.Flags |= LI_REMOVE;
1331-
D->Rhs.A.Flags |= LI_REMOVE;
1333+
/* Rhs load entries must be removed because Lhs must be in effect */
1334+
D->Rhs.X.Flags |= LI_MUST_REMOVE;
1335+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
13321336

13331337
/* Remove the push and the call to the operator function */
13341338
RemoveRemainders (D);
@@ -1396,8 +1400,8 @@ static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC)
13961400
X = NewCodeEntry (OPC, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI);
13971401
InsertEntry (D, X, D->IP++);
13981402

1399-
/* Rhs load entries must be removed */
1400-
D->Rhs.A.Flags |= LI_REMOVE;
1403+
/* Rhs A load must be removed because Lhs must be in effect */
1404+
D->Rhs.A.Flags |= (LI_REMOVE | LI_MUST_REMOVE);
14011405

14021406
} else if (RegIsDirectNonStackLoaded (&D->Lhs.A)) {
14031407

@@ -1419,12 +1423,10 @@ static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC)
14191423
InsertEntry (D, X, D->IP++);
14201424
}
14211425

1422-
/* ### Bug to come: there are dangerous Rhs X removal attempts here.
1423-
** Runtime function calls can be "loads", and must be treated as a
1424-
** single A/X unit, so removal of X load alone is not valid.
1425-
** This can be solved with an additional "removable Rhs" test, or
1426-
** by simply not forcing the LI_REMOVE flag and letting other optimizers
1427-
** remove any unnecessary loads.
1426+
/* Note: Eager Rhs X removals here can sometimes produce slightly worse
1427+
** code after all other optimizers have run. This may need a general
1428+
** study of which is better: eager removal, or letting other optimizers
1429+
** take care of unneeded loads.
14281430
*/
14291431
/* Do high-byte operation only when its result is used */
14301432
if ((GetRegInfo (D->Code, D->IP, REG_X) & REG_X) != 0) {
@@ -1433,10 +1435,12 @@ static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC)
14331435
/* Since this is a "same X" EOR, the result is always 0. */
14341436
X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->Rhs.X.ChgEntry->LI);
14351437
InsertEntry (D, X, D->IP++);
1438+
1439+
/* Rhs X load may be removed (but this is not a demand) */
14361440
D->Rhs.X.Flags |= LI_REMOVE;
14371441
}
14381442
} else {
1439-
/* Rhs load entries may be removed */
1443+
/* Rhs X load may be removed (but this is not a demand) */
14401444
D->Rhs.X.Flags |= LI_REMOVE;
14411445
}
14421446

@@ -1464,7 +1468,7 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer)
14641468

14651469
/* Rhs low-byte load must be removed and hi-byte load may be removed */
14661470
D->Rhs.X.Flags |= LI_REMOVE;
1467-
D->Rhs.A.Flags |= LI_REMOVE;
1471+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
14681472

14691473
} else if (RegIsDirectLoaded (&D->Lhs.A)) {
14701474
/* If the lhs is direct (but not stack relative), encode compares with lhs,
@@ -1604,7 +1608,9 @@ static unsigned Opt_a_tosicmp (StackOpData* D)
16041608
InsertEntry (D, X, D->IP++);
16051609
}
16061610

1607-
/* RHS may be removed */
1611+
/* RHS may be removed, but it is not a demand because Lhs was
1612+
** reloaded at Op and is in effect.
1613+
*/
16081614
D->Rhs.A.Flags |= LI_REMOVE;
16091615
D->Rhs.X.Flags |= LI_REMOVE;
16101616
}
@@ -1703,9 +1709,11 @@ static unsigned Opt_a_tossub (StackOpData* D)
17031709
InsertEntry (D, X, D->IP++);
17041710
}
17051711

1706-
/* Rhs load entries must be removed */
1712+
/* Rhs low-byte load must be removed (because Lhs must be in effect)
1713+
** and hi-byte load may be removed.
1714+
*/
17071715
D->Rhs.X.Flags |= LI_REMOVE;
1708-
D->Rhs.A.Flags |= LI_REMOVE;
1716+
D->Rhs.A.Flags |= LI_MUST_REMOVE;
17091717

17101718
/* Remove the push and the call to the tossubax function */
17111719
RemoveRemainders (D);

test/val/bug1374.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ int test1b(void)
2424
return (z == 0x89ab) ? 0 : 1;
2525
}
2626

27+
int test1c(void)
28+
{
29+
uint8_t x = 0x95;
30+
uint8_t y = 0xab;
31+
uint16_t z = (x * 0) | y;
32+
printf("%x\n", z);
33+
return (z == 0x00ab) ? 0 : 1;
34+
}
35+
2736
int test2(void)
2837
{
2938
uint16_t x = 0x8900;
@@ -33,6 +42,15 @@ int test2(void)
3342
return (z == 0x89ab) ? 0 : 1;
3443
}
3544

45+
int test2c(void)
46+
{
47+
uint16_t x = 0x6795;
48+
uint8_t y = 0xab;
49+
uint16_t z = (x * 0) | y;
50+
printf("%x\n", z);
51+
return (z == 0x00ab) ? 0 : 1;
52+
}
53+
3654
int test3(void)
3755
{
3856
uint16_t x = 0x89;
@@ -69,6 +87,15 @@ int test4b(void)
6987
return (z == 0x89ab) ? 0 : 1;
7088
}
7189

90+
int test4c(void)
91+
{
92+
uint8_t x = 0x95;
93+
uint16_t y = 0xabcd;
94+
uint16_t z = (x * 0) | y;
95+
printf("%x\n", z);
96+
return (z == 0xabcd) ? 0 : 1;
97+
}
98+
7299
int main(void)
73100
{
74101
res |= test1();
@@ -78,6 +105,9 @@ int main(void)
78105
res |= test1b();
79106
res |= test3b();
80107
res |= test4b();
108+
res |= test1c();
109+
res |= test2c();
110+
res |= test4c();
81111
printf("res: %d\n", res);
82112
return res;
83113
}

0 commit comments

Comments
 (0)