From 6e18e0880adcaf6201e5c805b3905a8b2e0f7924 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:59:02 +0200 Subject: [PATCH 1/3] Added/improved the optimizations: * Added a new pass that optimizes PHA/PLA sequences * Added a new pass that optimizes AND/EOR/ORA when an operand is known * Added a run of an existing pass at later stages to remove code that otherwise goes unchanged. * Handle binary operations in OptUnusedLoads in addition to real loads. --- src/cc65/codeent.c | 18 +++++ src/cc65/codeopt.c | 8 +++ src/cc65/coptind.c | 110 ++++++++++++++++++++++++++-- src/cc65/coptind.h | 7 +- src/cc65/coptmisc.c | 172 ++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptmisc.h | 6 ++ 6 files changed, 315 insertions(+), 6 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 928815934d..a870ee981d 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -2445,6 +2445,24 @@ static char* RegContentDesc (const RegContents* RC, char* Buf) sprintf (B, "Y:%02X ", RC->RegY); } B += 5; + if (RegValIsUnknown (RC->Ptr1Lo)) { + strcpy (B, "P1L:XX "); + } else { + sprintf (B, "P1L:%02X ", RC->Ptr1Lo); + } + B += 7; + if (RegValIsUnknown (RC->Ptr1Hi)) { + strcpy (B, "P1H:XX "); + } else { + sprintf (B, "P1H:%02X ", RC->Ptr1Hi); + } + B += 7; + if (RegValIsUnknown (RC->Tmp1)) { + strcpy (B, "T1:XX "); + } else { + sprintf (B, "T1:%02X ", RC->Tmp1); + } + B += 6; if (PStatesAreUnknown (RC->PFlags, PSTATE_C)) { strcpy (B, "~"); } else { diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index c9c1592bc0..a716ad4311 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -117,6 +117,7 @@ static OptFunc DOptBNegAX1 = { OptBNegAX1, "OptBNegAX1", 100, 0, static OptFunc DOptBNegAX2 = { OptBNegAX2, "OptBNegAX2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX3 = { OptBNegAX3, "OptBNegAX3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX4 = { OptBNegAX4, "OptBNegAX4", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBinOps = { OptBinOps, "OptBinOps", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolCmp = { OptBoolCmp, "OptBoolCmp", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolUnary1 = { OptBoolUnary1, "OptBoolUnary1", 40, 0, 0, 0, 0, 0 }; @@ -179,6 +180,7 @@ static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop1 = { OptPushPop1, "OptPushPop1", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop2 = { OptPushPop2, "OptPushPop2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop3 = { OptPushPop3, "OptPushPop3", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; @@ -231,6 +233,7 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX2, &DOptBNegAX3, &DOptBNegAX4, + &DOptBinOps, &DOptBoolCmp, &DOptBoolTrans, &DOptBoolUnary1, @@ -292,6 +295,8 @@ static OptFunc* OptFuncs[] = { &DOptPush1, &DOptPush2, &DOptPushPop1, + &DOptPushPop2, + &DOptPushPop3, &DOptRTS, &DOptRTSJumps1, &DOptRTSJumps2, @@ -740,9 +745,11 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptStore5, 1); C += RunOptFunc (S, &DOptPushPop1, 1); C += RunOptFunc (S, &DOptPushPop2, 1); + C += RunOptFunc (S, &DOptPushPop3, 1); C += RunOptFunc (S, &DOptPrecalc, 1); C += RunOptFunc (S, &DOptShiftBack, 1); C += RunOptFunc (S, &DOptSignExtended, 1); + C += RunOptFunc (S, &DOptBinOps, 1); Changes += C; @@ -849,6 +856,7 @@ static unsigned RunOptGroup7 (CodeSeg* S) Changes += RunOptFunc (S, &DOptUnusedStores, 1); Changes += RunOptFunc (S, &DOptJumpTarget1, 5); Changes += RunOptFunc (S, &DOptStore5, 1); + Changes += RunOptFunc (S, &DOptTransfers1, 1); } C = RunOptFunc (S, &DOptSize2, 1); diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 9e9985c100..49855a3452 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -151,7 +151,9 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) unsigned OptUnusedLoads (CodeSeg* S) -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ { unsigned Changes = 0; @@ -164,17 +166,24 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Check if it's a register load or transfer insn */ - if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { + /* Check if this is one of the instruction we can operate on */ + int IsOp = (E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 || + E->OPC == OP65_AND || + E->OPC == OP65_EOR || + E->OPC == OP65_ORA; + + /* Check for the necessary preconditions */ + if (IsOp && (N = CS_GetNextEntry (S, I)) != 0 && !CE_UseLoadFlags (N)) { /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { + case OP65_AND: case OP65_DEA: + case OP65_EOR: case OP65_INA: case OP65_LDA: + case OP65_ORA: case OP65_TXA: case OP65_TYA: R = REG_A; break; case OP65_DEX: @@ -1342,6 +1351,97 @@ unsigned OptPushPop2 (CodeSeg* S) +unsigned OptPushPop3 (CodeSeg* S) +/* Remove a pha/pla sequence where the contents of A are known */ +{ + unsigned Changes = 0; + unsigned Pha = 0; /* Index of PHA insn */ + unsigned Pla = 0; /* Index of PLA insn */ + CodeEntry* PhaEntry = 0; /* Pointer to PHA */ + + enum { + Searching, + FoundPha, + FoundPla + } State = Searching; + + /* Walk over the entries. Look for a PHA instruction where the contents + ** of A is known followed by a PLA later. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get the input registers */ + const RegInfo* RI = E->RI; + + + const char* Arg; + CodeEntry* X; + switch (State) { + + case Searching: + if (E->OPC == OP65_PHA && RegValIsKnown (RI->In.RegA)) { + /* Found start of sequence */ + Pha = I; + PhaEntry = E; + State = FoundPha; + } + break; + + case FoundPha: + /* Check for several things that abort the sequence: + ** - End of the basic block + ** - Another PHA or any other stack manipulating instruction + ** If we find something that aborts the sequence, start over + ** searching for the next PHA. + */ + if (CE_HasLabel (E)) { + /* Switch back to searching at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHA) { + /* Start over at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHP || E->OPC == OP65_PLP || E->OPC == OP65_TXS) { + /* Start over at the next instruction */ + State = Searching; + } else if (E->OPC == OP65_PLA) { + /* Switch state. This will also switch to the next insn + ** which is ok. + */ + Pla = I; + State = FoundPla; + } + break; + + case FoundPla: + /* We found the sequence we were looking for. Replace it. */ + Arg = MakeHexArg (PhaEntry->RI->In.RegA); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, Pla + 1); + CS_DelEntry (S, Pla); + CS_DelEntry (S, Pha); + ++Changes; + State = Searching; + break; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptPrecalc (CodeSeg* S) /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 6d39ee1f8d..3493543a46 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -50,7 +50,9 @@ unsigned OptUnusedLoads (CodeSeg* S); -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ @@ -91,6 +93,9 @@ unsigned OptPushPop1 (CodeSeg* S); unsigned OptPushPop2 (CodeSeg* S); /* Remove a PHP/PLP sequence were no processor flags changed inside */ +unsigned OptPushPop3 (CodeSeg* S); +/* Remove a pha/pla sequence where the contents of A are known */ + unsigned OptPrecalc (CodeSeg* S); /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 523fbf17cb..332247cf05 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -733,3 +733,175 @@ unsigned OptLoad2 (CodeSeg* S) /* Return the number of changes made */ return Changes; } + + + +unsigned OptBinOps (CodeSeg* S) +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + + /* Check for AND/EOR/ORA and a known value in A */ + int Delete = 0; + CodeEntry* X = 0; + switch (E->OPC) { + + case OP65_AND: + if (In->RegA == 0x00) { + /* Zero AND anything gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (In->RegA == 0xFF) { + /* 0xFF AND anything equals the operand. The instruction + ** can be replaced by a simple load of the operand. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* AND with zero gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (Operand == 0xFF) { + /* AND with 0xFF is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** AND. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_AND, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_EOR: + if (In->RegA == 0x00) { + /* Zero EOR anything equals the operand. The instruction + ** can be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* EOR with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** EOR. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_EOR, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_ORA: + if (In->RegA == 0x00) { + /* ORA with 0x00 is a no-op. The instruction can be + ** replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (In->RegA == 0xFF) { + /* ORA with 0xFF gives 0xFF. The instruction can be replaced + ** by an immediate load of 0xFF. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* ORA with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + CodeEntry* N = CS_GetNextEntry (S, I); + if (N && !CE_UseLoadFlags (N)) { + Delete = 1; + } + } else if (Operand == 0xFF) { + /* ORA with 0xFF results in 0xFF. The instruction can + ** be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** ORA. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_ORA, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + default: + break; + + } + + /* If we must delete the instruction, do that. If we have a replacement + ** entry, place it and remove the old one. + */ + if (X) { + CS_InsertEntry (S, X, I+1); + Delete = 1; + } + if (Delete) { + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h index 89242351ce..418b61e943 100644 --- a/src/cc65/coptmisc.h +++ b/src/cc65/coptmisc.h @@ -107,6 +107,12 @@ unsigned OptLoad1 (CodeSeg* S); unsigned OptLoad2 (CodeSeg* S); /* Replace calls to ldaxysp by inline code */ +unsigned OptBinOps (CodeSeg* S); +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ + + /* End of coptmisc.h */ From f43cfd1ad0ad2f668a5808ad000bdc2feea5d6da Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:19:13 +0200 Subject: [PATCH 2/3] Fix the check for CPU flags being used after an instruction that gets removed. Previously only the next instruction was checked for usage of the CPU flags but this fails for certain code. --- src/cc65/codeinfo.c | 24 +++++++++++++----------- src/cc65/codeinfo.h | 3 +++ src/cc65/coptind.c | 6 +++--- src/cc65/coptmisc.c | 9 +++------ 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 427bfc52b2..435794613c 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -560,11 +560,8 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) *Use = REG_NONE; } - /* Will destroy all registers */ - *Chg = REG_ALL; - - /* and will destroy all processor flags */ - *Chg |= PSTATE_ALL; + /* Will destroy all registers and processor flags */ + *Chg = (REG_ALL | PSTATE_ALL); /* Done */ return FNCLS_GLOBAL; @@ -577,8 +574,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** are used mostly in inline assembly anyway. */ *Use = REG_ALL; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); return FNCLS_NUMERIC; } else { @@ -605,8 +601,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) fprintf (stderr, "No info about internal function '%s'\n", Name); } *Use = REG_ALL; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); } return FNCLS_BUILTIN; } @@ -615,8 +610,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** registers and processor flags are changed */ *Use = REG_EAXY; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); return FNCLS_UNKNOWN; } @@ -899,6 +893,14 @@ int RegEAXUsed (struct CodeSeg* S, unsigned Index) +int LoadFlagsUsed (struct CodeSeg* S, unsigned Index) +/* Check if one of the flags set by a register load (Z and N) are used. */ +{ + return (GetRegInfo (S, Index, PSTATE_ZN) & PSTATE_ZN) != 0; +} + + + unsigned GetKnownReg (unsigned Use, const RegContents* RC) /* Return the register or zero page location from the set in Use, thats ** contents are known. If Use does not contain any register, or if the diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 14ef54d8f3..c57908dad4 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -201,6 +201,9 @@ int RegAXUsed (struct CodeSeg* S, unsigned Index); int RegEAXUsed (struct CodeSeg* S, unsigned Index); /* Check if any of the four bytes in EAX are used. */ +int LoadFlagsUsed (struct CodeSeg* S, unsigned Index); +/* Check if one of the flags set by a register load (Z and N) are used. */ + unsigned GetKnownReg (unsigned Use, const struct RegContents* RC); /* Return the register or zero page location from the set in Use, thats ** contents are known. If Use does not contain any register, or if the diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 49855a3452..52c47481e6 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -151,8 +151,8 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) unsigned OptUnusedLoads (CodeSeg* S) -/* Remove loads of or operations with registers where the value loaded or -** produced is not used later. +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. */ { unsigned Changes = 0; @@ -173,7 +173,7 @@ unsigned OptUnusedLoads (CodeSeg* S) E->OPC == OP65_ORA; /* Check for the necessary preconditions */ - if (IsOp && (N = CS_GetNextEntry (S, I)) != 0 && !CE_UseLoadFlags (N)) { + if (IsOp && (N = CS_GetNextEntry (S, I)) != 0 && !LoadFlagsUsed (S, I+1)) { /* Check which sort of load or transfer it is */ unsigned R; diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 332247cf05..e48d469a1c 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -788,8 +788,7 @@ unsigned OptBinOps (CodeSeg* S) ** The instruction can be removed if the flags aren't ** used later. */ - CodeEntry* N = CS_GetNextEntry (S, I); - if (N && !CE_UseLoadFlags (N)) { + if (!LoadFlagsUsed (S, I+1)) { Delete = 1; } } else if (Operand >= 0) { @@ -822,8 +821,7 @@ unsigned OptBinOps (CodeSeg* S) ** The instruction can be removed if the flags aren't ** used later. */ - CodeEntry* N = CS_GetNextEntry (S, I); - if (N && !CE_UseLoadFlags (N)) { + if (!LoadFlagsUsed (S, I+1)) { Delete = 1; } } else if (Operand >= 0) { @@ -861,8 +859,7 @@ unsigned OptBinOps (CodeSeg* S) ** The instruction can be removed if the flags aren't ** used later. */ - CodeEntry* N = CS_GetNextEntry (S, I); - if (N && !CE_UseLoadFlags (N)) { + if (!LoadFlagsUsed (S, I+1)) { Delete = 1; } } else if (Operand == 0xFF) { From 231ab4169bd54696d3742214eb73a6527f9b7ff1 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:20:27 +0200 Subject: [PATCH 3/3] Added a test. --- test/val/and-eor-ora-optimizations.c | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/val/and-eor-ora-optimizations.c diff --git a/test/val/and-eor-ora-optimizations.c b/test/val/and-eor-ora-optimizations.c new file mode 100644 index 0000000000..6b5aba68c5 --- /dev/null +++ b/test/val/and-eor-ora-optimizations.c @@ -0,0 +1,86 @@ +/* Test some new optimization passes */ + +#include + +static unsigned failures = 0; + +int func0() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a & 0x00FF) & (b & 0xFF00); +} + +int func1() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF & a) & (0xFF00 & b); +} + +int func2() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) & (b | 0xFF00); +} + +int func3() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) & (0xFF00 | b); +} + +int func4() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) | (b | 0xFF00); +} + +int func5() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) | (0xFF00 | b); +} + +int func6() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a ^ 0x00FF) & (b & 0xFF00); +} + +int func7() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF ^ a) & (0xFF00 & b); +} + +int func8() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) | (b ^ 0xFF00); +} + +int func9() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) | (0xFF00 ^ b); +} + +void onetest(unsigned count, int (*f1)(void), int (*f2)(void), int result) +{ + int r1 = f1(); + int r2 = f2(); + if (r1 != result || r2 != result) { + printf("Test %u failed! Expected 0x%04X but got 0x%04X/0x%04X\n", + count, result, r1, r2); + ++failures; + } +} + +int main() +{ + onetest(1, func0, func1, 0x0000); + onetest(2, func2, func3, 0x12AA); + onetest(3, func4, func5, 0xFFFF); + onetest(4, func6, func7, 0x1000); + onetest(5, func8, func9, 0xBAFF); + return failures; +}