1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-08 15:29:37 +00:00

Merge branch 'master' into fptest

This commit is contained in:
mrdudz 2023-10-26 16:51:33 +02:00
commit 1db3d87ac8
29 changed files with 2245 additions and 1193 deletions

View File

@ -31,10 +31,8 @@
; bgt - jump if unsigned greater
.macro bgt Arg
.local L
beq L
beq *+4
bcs Arg
L:
.endmacro
; ble - jump if unsigned less or equal

View File

@ -837,21 +837,21 @@ This cc65 version has some extensions to the ISO C standard.
<itemize>
<item> The compiler allows to insert assembler expressions into the output
file. The syntax is
<item> The compiler allows to insert inline assembler code in the form of the
<tt/asm/ expression into the output file. The syntax is
<tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
or
<tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
The first form is in the user namespace; and, is disabled if the <tt/-A/
switch is given.
There is a whole section covering inline assembler expressions,
There is a whole section covering the inline assembler,
<ref id="inline-asm" name="see there">.
<p>
@ -1008,6 +1008,13 @@ This cc65 version has some extensions to the ISO C standard.
<tt/_Static_assert/ is also available as the macro <tt/static_assert/ in
<tt/assert.h/.
Note: The string literal as the message in the <tt/_Static_assert/
declaration is not subject to string literal translation (see
<tt/<ref id="pragma-charmap" name="#pragma&nbsp;charmap()">/) and will
always be in the host encoding. On the other hand, any character or
string literals present in the condition expression of the
<tt/_Static_assert/ declaration will be translated as usual.
<item> cc65 supports bit-fields of any integral type that is int-sized or
smaller, and enumerated types with those types as their underlying
type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are
@ -1317,7 +1324,9 @@ parameter with the <tt/#pragma/.
<sect1><tt>#pragma charmap (&lt;index&gt;, &lt;code&gt;)</tt><label id="pragma-charmap"><p>
Each literal string and each literal character in the source is translated
Each literal string and each literal character in the preprocessed source,
except when used in an <tt/asm/ expression as the inline assembler code or
in a <tt/_Static_assert/ declaration as the failure message, is translated
by use of a translation table. That translation table is preset when the
compiler is started, depending on the target system; for example, to map
ISO-8859-1 characters into PETSCII if the target is a Commodore machine.
@ -1714,23 +1723,23 @@ bloated code and a slowdown.
<sect>Inline assembler<label id="inline-asm"><p>
The compiler allows to insert assembler expressions into the output file. The
syntax is
The compiler allows to insert inline assembler code in the form of the <tt/asm/
expression into the output file. The syntax is
<tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
or
<tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
<p>
The first form is in the user namespace; and, is disabled by <tt><ref
id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/.
The <tt/asm/ expression can be used only inside a function. Please note that
the result of an inline assembler expression is always of type <tt/void/.
The <tt/asm/ expression can be used only inside a function. The result of an
<tt/asm/ expression is always of type <tt/void/.
The contents of the string literal are preparsed by the compiler; and, inserted
into the generated assembly output, so that it can be processed further by
@ -1757,6 +1766,13 @@ The string literal may contain format specifiers from the following list. For
each format specifier, an argument is expected which is inserted instead of
the format specifier, before passing the assembly code line to the backend.
Note: The string literal as the inline assembler code itself in the <tt/asm/
expression is not subject to string literal translation (see
<tt/<ref id="pragma-charmap" name="#pragma&nbsp;charmap()">/) and will always
be in the host encoding. On the other hand, all character and string literals
as the arguments for replacing the format specifiers will be translated as
usual.
<itemize>
<item><tt/%b/ - Numerical 8-bit value
<item><tt/%w/ - Numerical 16-bit value

View File

@ -70,6 +70,8 @@ ACIA_STATUS := ACIA+1 ; Status register
ACIA_CMD := ACIA+2 ; Command register
ACIA_CTRL := ACIA+3 ; Control register
SLTROMSEL := $C02D ; For Apple IIgs slot verification
;----------------------------------------------------------------------------
; Global variables
@ -141,35 +143,16 @@ ParityTable: ; Table used to translate RS232 parity param
.byte $A0 ; SER_PAR_MARK
.byte $E0 ; SER_PAR_SPACE
IdOfsTable: ; Table of bytes positions, used to check five
IdOfsTable: ; Table of bytes positions, used to check four
; specific bytes on the slot's firmware to make
; sure this is an SSC (or Apple //c comm port)
; firmware that drives an ACIA 6551 chip.
;
; The SSC firmware and the Apple //c(+) comm
; port firmware all begin with a BIT instruction.
; The IIgs, on the other hand, has a
; Zilog Z8530 chip and its firmware starts with
; a SEP instruction. We don't want to load this
; driver on the IIgs' serial port. We'll
; differentiate the firmware on this byte.
;
; The next four bytes we check are the Pascal
; Firmware Protocol Bytes that identify a
; serial card. Those are the same bytes for
; SSC firmwares, Apple //c firmwares and IIgs
; Zilog Z8530 firmwares - which is the reason
; we have to check for the firmware's first
; instruction too.
.byte $00 ; First instruction
; sure this is a serial card.
.byte $05 ; Pascal 1.0 ID byte
.byte $07 ; Pascal 1.0 ID byte
.byte $0B ; Pascal 1.1 generic signature byte
.byte $0C ; Device signature byte
IdValTable: ; Table of expected values for the five checked
IdValTable: ; Table of expected values for the four checked
; bytes
.byte $2C ; BIT
.byte $38 ; ID Byte 0 (from Pascal 1.0), fixed
.byte $18 ; ID Byte 1 (from Pascal 1.0), fixed
.byte $01 ; Generic signature for Pascal 1.1, fixed
@ -213,9 +196,32 @@ SER_CLOSE:
;----------------------------------------------------------------------------
; SER_OPEN: A pointer to a ser_params structure is passed in ptr1.
; Must return an SER_ERR_xx code in a/x.
; Note: Hardware checks are done in SER_OPEN instead of SER_INSTALL,
; because they depend on the selected slot, and we can't select the slot
; before SER_INSTALL.
SER_OPEN:
ldx #<$C000
; Check if this is a IIgs (Apple II Miscellaneous TechNote #7,
; Apple II Family Identification)
sec
bit $C082
jsr $FE1F
bit $C080
bcs NotIIgs
; We're on a IIgs. For every slot N, either bit N of $C02D is
; 0 for the internal ROM, or 1 for "Your Card". Let's make sure
; that slot N's bit is set to 1, otherwise, that can't be an SSC.
ldy Slot
lda SLTROMSEL
: lsr
dey
bpl :- ; Shift until slot's bit ends in carry
bcc NoDev
NotIIgs:ldx #<$C000
stx ptr2
lda #>$C000
ora Slot
@ -239,12 +245,44 @@ SER_OPEN:
.endif
tax
; Check that this works like an ACIA 6551 is expected to work
lda ACIA_STATUS,x ; Save current values in what we expect to be
sta tmp1 ; the ACIA status register
lda ACIA_CMD,x ; and command register. So we can restore them
sta tmp2 ; if this isn't a 6551.
ldy #%00000010 ; Disable TX/RX, disable IRQ
: tya
sta ACIA_CMD,x
cmp ACIA_CMD,x ; Verify what we stored is there
bne NotAcia
iny ; Enable TX/RX, disable IRQ
cpy #%00000100
bne :-
sta ACIA_STATUS,x ; Reset ACIA
lda ACIA_CMD,x ; Check that RX/TX is disabled
lsr
bcc AciaOK
NotAcia:lda tmp2 ; Restore original values
sta ACIA_CMD,x
lda tmp1
sta ACIA_STATUS,x
NoDev: lda #SER_ERR_NO_DEVICE
bne Out
; Check if the handshake setting is valid
ldy #SER_PARAMS::HANDSHAKE
AciaOK: ldy #SER_PARAMS::HANDSHAKE
lda (ptr1),y
cmp #SER_HS_HW ; This is all we support
bne InvParm
beq HandshakeOK
lda #SER_ERR_INIT_FAILED
bne Out
HandshakeOK:
ldy #$00 ; Initialize buffers
sty Stopped
sty RecvHead
@ -261,9 +299,12 @@ SER_OPEN:
lda (ptr1),y ; Baudrate index
tay
lda BaudTable,y ; Get 6551 value
bmi InvBaud ; Branch if rate not supported
sta tmp1
bpl BaudOK ; Check that baudrate is supported
lda #SER_ERR_BAUD_UNAVAIL
bne Out
BaudOK: sta tmp1
ldy #SER_PARAMS::DATABITS
lda (ptr1),y ; Databits index
tay
@ -294,22 +335,7 @@ SER_OPEN:
; Done
stx Index ; Mark port as open
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax
rts
; Device (hardware) not found
NoDev: lda #SER_ERR_NO_DEVICE
ldx #$00 ; Promote char return value
rts
; Invalid parameter
InvParm:lda #SER_ERR_INIT_FAILED
ldx #$00 ; Promote char return value
rts
; Baud rate not available
InvBaud:lda #SER_ERR_BAUD_UNAVAIL
Out:
ldx #$00 ; Promote char return value
rts
@ -479,4 +505,4 @@ Send: ldy SendHead ; Get first byte to send
sta ACIA_DATA,x ; Send it
inc SendHead
inc SendFreeCnt
jmp NextByte ; And try next one
bne NextByte ; And try next one

View File

@ -64,12 +64,12 @@
<ClInclude Include="cc65\codeseg.h" />
<ClInclude Include="cc65\compile.h" />
<ClInclude Include="cc65\coptadd.h" />
<ClInclude Include="cc65\coptbool.h" />
<ClInclude Include="cc65\coptc02.h" />
<ClInclude Include="cc65\coptcmp.h" />
<ClInclude Include="cc65\coptind.h" />
<ClInclude Include="cc65\coptjmp.h" />
<ClInclude Include="cc65\coptmisc.h" />
<ClInclude Include="cc65\coptneg.h" />
<ClInclude Include="cc65\coptptrload.h" />
<ClInclude Include="cc65\coptptrstore.h" />
<ClInclude Include="cc65\coptpush.h" />
@ -79,6 +79,7 @@
<ClInclude Include="cc65\coptstore.h" />
<ClInclude Include="cc65\coptsub.h" />
<ClInclude Include="cc65\copttest.h" />
<ClInclude Include="cc65\coptunary.h" />
<ClInclude Include="cc65\dataseg.h" />
<ClInclude Include="cc65\datatype.h" />
<ClInclude Include="cc65\declare.h" />
@ -144,12 +145,12 @@
<ClCompile Include="cc65\codeseg.c" />
<ClCompile Include="cc65\compile.c" />
<ClCompile Include="cc65\coptadd.c" />
<ClCompile Include="cc65\coptbool.c" />
<ClCompile Include="cc65\coptc02.c" />
<ClCompile Include="cc65\coptcmp.c" />
<ClCompile Include="cc65\coptind.c" />
<ClCompile Include="cc65\coptjmp.c" />
<ClCompile Include="cc65\coptmisc.c" />
<ClCompile Include="cc65\coptneg.c" />
<ClCompile Include="cc65\coptptrload.c" />
<ClCompile Include="cc65\coptptrstore.c" />
<ClCompile Include="cc65\coptpush.c" />
@ -159,6 +160,7 @@
<ClCompile Include="cc65\coptstore.c" />
<ClCompile Include="cc65\coptsub.c" />
<ClCompile Include="cc65\copttest.c" />
<ClCompile Include="cc65\coptunary.c" />
<ClCompile Include="cc65\dataseg.c" />
<ClCompile Include="cc65\datatype.c" />
<ClCompile Include="cc65\declare.c" />

View File

@ -417,6 +417,9 @@ void AsmStatement (void)
** a string literal in parenthesis.
*/
{
/* Prevent from translating the inline code string literal in asm */
NoCharMap = 1;
/* Skip the ASM */
NextToken ();
@ -431,9 +434,15 @@ void AsmStatement (void)
/* Need left parenthesis */
if (!ConsumeLParen ()) {
NoCharMap = 0;
return;
}
/* We have got the inline code string untranslated, now reenable string
** literal translation for string arguments (if any).
*/
NoCharMap = 0;
/* String literal */
if (CurTok.Tok != TOK_SCONST) {

View File

@ -52,12 +52,12 @@
#include "codeinfo.h"
#include "codeopt.h"
#include "coptadd.h"
#include "coptbool.h"
#include "coptc02.h"
#include "coptcmp.h"
#include "coptind.h"
#include "coptjmp.h"
#include "coptmisc.h"
#include "coptneg.h"
#include "coptptrload.h"
#include "coptptrstore.h"
#include "coptpush.h"
@ -67,6 +67,7 @@
#include "coptstore.h"
#include "coptsub.h"
#include "copttest.h"
#include "coptunary.h"
#include "error.h"
#include "global.h"
#include "output.h"
@ -115,7 +116,11 @@ 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 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 };
static OptFunc DOptBoolUnary2 = { OptBoolUnary2, "OptBoolUnary2", 40, 0, 0, 0, 0, 0 };
static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 0, 0, 0, 0, 0 };
static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 0, 0, 0, 0, 0, 0 };
static OptFunc DOptBranchDist2 = { OptBranchDist2, "OptBranchDist2", 0, 0, 0, 0, 0, 0 };
static OptFunc DOptCmp1 = { OptCmp1, "OptCmp1", 42, 0, 0, 0, 0, 0 };
@ -123,13 +128,14 @@ static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0,
static OptFunc DOptCmp3 = { OptCmp3, "OptCmp3", 75, 0, 0, 0, 0, 0 };
static OptFunc DOptCmp4 = { OptCmp4, "OptCmp4", 75, 0, 0, 0, 0, 0 };
static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 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 DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 };
static OptFunc DOptCmp9 = { OptCmp9, "OptCmp9", 85, 0, 0, 0, 0, 0 };
static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 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 DOptCondBranch1 = { OptCondBranch1, "OptCondBranch1", 80, 0, 0, 0, 0, 0 };
static OptFunc DOptCondBranch2 = { OptCondBranch2, "OptCondBranch2", 40, 0, 0, 0, 0, 0 };
static OptFunc DOptCondBranch3 = { OptCondBranch3, "OptCondBranch3", 40, 0, 0, 0, 0, 0 };
static OptFunc DOptCondBranchC = { OptCondBranchC, "OptCondBranchC", 0, 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 DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 };
@ -221,7 +227,11 @@ static OptFunc* OptFuncs[] = {
&DOptBNegAX2,
&DOptBNegAX3,
&DOptBNegAX4,
&DOptBoolCmp,
&DOptBoolTrans,
&DOptBoolUnary1,
&DOptBoolUnary2,
&DOptBoolUnary3,
&DOptBranchDist,
&DOptBranchDist2,
&DOptCmp1,
@ -229,13 +239,14 @@ static OptFunc* OptFuncs[] = {
&DOptCmp3,
&DOptCmp4,
&DOptCmp5,
&DOptCmp6,
&DOptCmp7,
&DOptCmp8,
&DOptCmp9,
&DOptComplAX1,
&DOptCondBranches1,
&DOptCondBranches2,
&DOptCondBranch1,
&DOptCondBranch2,
&DOptCondBranch3,
&DOptCondBranchC,
&DOptDeadCode,
&DOptDeadJumps,
&DOptDecouple,
@ -612,10 +623,6 @@ static unsigned RunOptGroup1 (CodeSeg* S)
Changes += RunOptFunc (S, &DOptPtrLoad15, 1);
Changes += RunOptFunc (S, &DOptPtrLoad16, 1);
Changes += RunOptFunc (S, &DOptPtrLoad17, 1);
Changes += RunOptFunc (S, &DOptBNegAX1, 1);
Changes += RunOptFunc (S, &DOptBNegAX2, 1);
Changes += RunOptFunc (S, &DOptBNegAX3, 1);
Changes += RunOptFunc (S, &DOptBNegAX4, 1);
Changes += RunOptFunc (S, &DOptAdd1, 1);
Changes += RunOptFunc (S, &DOptAdd2, 1);
Changes += RunOptFunc (S, &DOptAdd4, 1);
@ -668,11 +675,15 @@ static unsigned RunOptGroup3 (CodeSeg* S)
do {
C = 0;
C += RunOptFunc (S, &DOptBNegA1, 1);
C += RunOptFunc (S, &DOptBNegA2, 1);
C += RunOptFunc (S, &DOptNegAX1, 1);
C += RunOptFunc (S, &DOptNegAX2, 1);
C += RunOptFunc (S, &DOptStackOps, 3);
C += RunOptFunc (S, &DOptStackOps, 3); /* Before OptBoolUnary1 */
C += RunOptFunc (S, &DOptCmp8, 1); /* Before OptBoolUnary1 */
C += RunOptFunc (S, &DOptBoolUnary1, 3);
C += RunOptFunc (S, &DOptBoolUnary2, 3);
C += RunOptFunc (S, &DOptBoolUnary3, 1);
C += RunOptFunc (S, &DOptBNegA1, 1);
C += RunOptFunc (S, &DOptBNegAX1, 1); /* After OptBoolUnary2 */
C += RunOptFunc (S, &DOptShift1, 1);
C += RunOptFunc (S, &DOptShift4, 1);
C += RunOptFunc (S, &DOptComplAX1, 1);
@ -684,24 +695,30 @@ static unsigned RunOptGroup3 (CodeSeg* S)
C += RunOptFunc (S, &DOptJumpCascades, 1);
C += RunOptFunc (S, &DOptDeadJumps, 1);
C += RunOptFunc (S, &DOptDeadCode, 1);
C += RunOptFunc (S, &DOptBoolTrans, 1);
C += RunOptFunc (S, &DOptJumpTarget1, 1);
C += RunOptFunc (S, &DOptJumpTarget2, 1);
C += RunOptFunc (S, &DOptCondBranches1, 1);
C += RunOptFunc (S, &DOptCondBranches2, 1);
C += RunOptFunc (S, &DOptCondBranch1, 1);
C += RunOptFunc (S, &DOptCondBranch2, 1);
C += RunOptFunc (S, &DOptCondBranch3, 1);
C += RunOptFunc (S, &DOptCondBranchC, 1);
C += RunOptFunc (S, &DOptRTSJumps1, 1);
C += RunOptFunc (S, &DOptBoolCmp, 1);
C += RunOptFunc (S, &DOptBoolTrans, 1);
C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */
C += RunOptFunc (S, &DOptBNegAX2, 1); /* After OptCondBranch's */
C += RunOptFunc (S, &DOptBNegAX3, 1); /* After OptCondBranch's */
C += RunOptFunc (S, &DOptBNegAX4, 1); /* After OptCondBranch's */
C += RunOptFunc (S, &DOptCmp1, 1);
C += RunOptFunc (S, &DOptCmp2, 1);
C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */
C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */
C += RunOptFunc (S, &DOptCmp3, 1);
C += RunOptFunc (S, &DOptCmp4, 1);
C += RunOptFunc (S, &DOptCmp5, 1);
C += RunOptFunc (S, &DOptCmp6, 1);
C += RunOptFunc (S, &DOptCmp7, 1);
C += RunOptFunc (S, &DOptCmp9, 1);
C += RunOptFunc (S, &DOptTest1, 1);
C += RunOptFunc (S, &DOptLoad1, 1);
C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */
C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */
C += RunOptFunc (S, &DOptUnusedLoads, 1);
C += RunOptFunc (S, &DOptUnusedStores, 1);
C += RunOptFunc (S, &DOptDupLoads, 1);

View File

@ -111,12 +111,6 @@ static void Parse (void)
continue;
}
/* Check for a #pragma */
if (CurTok.Tok == TOK_PRAGMA) {
DoPragma ();
continue;
}
/* Check for a _Static_assert */
if (CurTok.Tok == TOK_STATIC_ASSERT) {
ParseStaticAssert ();

942
src/cc65/coptbool.c Normal file
View File

@ -0,0 +1,942 @@
/*****************************************************************************/
/* */
/* coptbool.c */
/* */
/* Optimize boolean sequences */
/* */
/* */
/* */
/* (C) 2001-2012, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* 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. */
/* */
/*****************************************************************************/
/* cc65 */
#include "codeent.h"
#include "codeinfo.h"
#include "error.h"
#include "coptbool.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Table used to invert a condition, indexed by condition */
static const unsigned char CmpInvertTab[] = {
CMP_NE, CMP_EQ,
CMP_LE, CMP_LT, CMP_GE, CMP_GT,
CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
};
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond)
/* Helper function for the replacement of routines that return a boolean
** followed by a conditional jump. Instead of the boolean value, the condition
** codes are evaluated directly.
** I is the index of the conditional branch, the sequence is already checked
** to be correct.
*/
{
CodeEntry* N;
CodeLabel* L;
/* Get the entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Replace the conditional branch */
switch (Cond) {
case CMP_EQ:
CE_ReplaceOPC (E, OP65_JEQ);
break;
case CMP_NE:
CE_ReplaceOPC (E, OP65_JNE);
break;
case CMP_GT:
/* Replace by
** beq @L
** jpl Target
** @L: ...
*/
if ((N = CS_GetNextEntry (S, I)) == 0) {
/* No such entry */
Internal ("Invalid program flow");
}
L = CS_GenLabel (S, N);
N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I);
CE_ReplaceOPC (E, OP65_JPL);
break;
case CMP_GE:
CE_ReplaceOPC (E, OP65_JPL);
break;
case CMP_LT:
CE_ReplaceOPC (E, OP65_JMI);
break;
case CMP_LE:
/* Replace by
** jmi Target
** jeq Target
*/
CE_ReplaceOPC (E, OP65_JMI);
L = E->JumpTo;
N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I+1);
break;
case CMP_UGT:
/* Replace by
** beq @L
** jcs Target
** @L: ...
*/
if ((N = CS_GetNextEntry (S, I)) == 0) {
/* No such entry */
Internal ("Invalid program flow");
}
L = CS_GenLabel (S, N);
N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I);
CE_ReplaceOPC (E, OP65_JCS);
break;
case CMP_UGE:
CE_ReplaceOPC (E, OP65_JCS);
break;
case CMP_ULT:
CE_ReplaceOPC (E, OP65_JCC);
break;
case CMP_ULE:
/* Replace by
** jcc Target
** jeq Target
*/
CE_ReplaceOPC (E, OP65_JCC);
L = E->JumpTo;
N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I+1);
break;
default:
Internal ("Unknown jump condition: %d", Cond);
}
}
/*****************************************************************************/
/* Optimize bool comparison and transformer subroutines with branches */
/*****************************************************************************/
unsigned OptBoolCmp (CodeSeg* S)
/* Search for calls to compare subroutines followed by a conditional branch
** and replace them by cheaper versions, since the branch means that the
** boolean value returned by these routines is not needed (we may also check
** that explicitly, but for the current code generator it is always true).
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
cmp_t Cond;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_JSR &&
(Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (N)) {
/* The tos... functions will return a boolean value in a/x and
** the Z flag says if this value is zero or not. We will call
** a cheaper subroutine instead, one that does not return a
** boolean value but only valid flags. Note: jeq jumps if
** the condition is not met, jne jumps if the condition is met.
** Invert the code if we jump on condition not met.
*/
if (GetBranchCond (N->OPC) == BC_EQ) {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* Replace the subroutine call. */
E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
CS_InsertEntry (S, E, I+1);
CS_DelEntry (S, I);
/* Replace the conditional branch */
ReplaceBranchCond (S, I+1, Cond);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBoolTrans (CodeSeg* S)
/* Try to remove the call to boolean transformer routines where the call is
** not really needed and change following branch condition accordingly.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
cmp_t Cond;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for a boolean transformer */
if (E->OPC == OP65_JSR &&
(Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_ZBRA) != 0 &&
(GetRegInfo (S, I + 2, PSTATE_Z) & PSTATE_Z) == 0) {
/* Make the boolean transformer unnecessary by changing the
** the conditional jump to evaluate the condition flags that
** are set after the compare directly. Note: jeq jumps if
** the condition is not met, jne jumps if the condition is met.
** Invert the code if we jump on condition not met.
*/
if (GetBranchCond (N->OPC) == BC_EQ) {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* Check if we can replace the code by something better */
ReplaceBranchCond (S, I+1, Cond);
/* Remove the call to the bool transformer */
CS_DelEntry (S, I);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBoolUnary (CodeSeg* S)
/* Try to remove the call to a bcastax/bnegax routines where the call is
** not really needed and change following branch condition accordingly.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
cmp_t Cond;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for a boolean transformer */
if (E->OPC == OP65_JSR &&
(Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_ZBRA) != 0) {
/* Make the boolean transformer unnecessary by changing the
** the conditional jump to evaluate the condition flags that
** are set after the compare directly. Note: jeq jumps if
** the condition is not met, jne jumps if the condition is met.
** Invert the code if we jump on condition not met.
*/
if (GetBranchCond (N->OPC) == BC_EQ) {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* Check if we can replace the code by something better */
ReplaceBranchCond (S, I+1, Cond);
/* Remove the call to the bool transformer */
CS_DelEntry (S, I);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* Remove calls to the boolean cast/negation subroutines */
/*****************************************************************************/
unsigned OptBoolUnary1 (CodeSeg* S)
/* Search for and remove cmp #0/bcastax/boolne following a bcastax/bnegax.
** Or search for and remove cmp #1/bnegax/booleq following a bcastax/bnegax
** and invert the bcastax/bnegax.
*/
{
unsigned Changes = 0;
int Neg = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence.
** We allow the first entry to have labels.
*/
if (L[0]->OPC == OP65_JSR &&
(L[1] = CS_GetNextEntry (S, I)) != 0 &&
!CE_HasLabel (L[1])) {
if (strcmp (L[0]->Arg, "bnegax") == 0) {
Neg = 1;
} else if (strcmp (L[0]->Arg, "bcastax") == 0) {
Neg = 0;
} else {
/* Next entry */
++I;
continue;
}
if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) ||
CE_IsCallTo (L[1], "boolne") ||
CE_IsCallTo (L[1], "bcastax")) {
/* Delete the entry no longer needed. */
CS_DelEntry (S, I + 1);
/* Remember, we had changes */
++Changes;
/* We are still at this index */
continue;
} else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) ||
CE_IsCallTo (L[1], "booleq") ||
CE_IsCallTo (L[1], "bnegax")) {
/* Invert the previous bool conversion */
CE_SetArg (L[0], Neg ? "bcastax" : "bnegax");
/* Delete the entry no longer needed */
CS_DelEntry (S, I + 1);
/* Remember, we had changes */
++Changes;
/* We are still at this index */
continue;
}
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBoolUnary2 (CodeSeg* S)
/* Search for and remove cmp #0/bcastax/boolne following a boolean transformer.
** Or search for and remove cmp #1/bnegax/booleq following a boolean transformer
** and invert the boolean transformer.
*/
{
unsigned Changes = 0;
cmp_t Cond;
char Buf[16];
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence.
** We allow the first entry to have labels.
*/
if (L[0]->OPC == OP65_JSR &&
(L[1] = CS_GetNextEntry (S, I)) != 0 &&
!CE_HasLabel (L[1]) &&
(Cond = FindBoolCmpCond (L[0]->Arg)) != CMP_INV) {
if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) ||
CE_IsCallTo (L[1], "boolne") ||
CE_IsCallTo (L[1], "bcastax")) {
/* Delete the entry no longer needed */
CS_DelEntry (S, I + 1);
/* Remember, we had changes */
++Changes;
/* We are still at this index */
continue;
} else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) ||
CE_IsCallTo (L[1], "booleq") ||
CE_IsCallTo (L[1], "bnegax")) {
/* Invert the bool conversion */
if (GetBoolCmpSuffix (Buf, GetNegatedCond (Cond)) == 0) {
Internal ("No inverted boolean transformer for: %s", L[0]->Arg);
}
CE_SetArg (L[0], Buf);
/* Delete the entry no longer needed */
CS_DelEntry (S, I + 1);
/* Remember, we had changes */
++Changes;
/* We are still at this index */
continue;
}
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBoolUnary3 (CodeSeg* S)
/* If A == 0, replace bcastax/bnegax with
**
** cpx #0
** jsr boolne/booleq
**
** Or if X == 0, replace bcastax/bnegax with
**
** cmp #0
** jsr boolne/booleq
**
*/
{
unsigned Changes = 0;
opc_t Op = OP65_COUNT;
const char* Sub = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* E;
CodeEntry* X;
/* Get next entry */
E = CS_GetEntry (S, I);
/* Check for the sequence */
if (!CE_HasLabel (E)) {
/* Choose the right subroutine */
if (CE_IsCallTo (E, "bnegax")) {
Sub = "booleq";
} else if (CE_IsCallTo (E, "bcastax")) {
Sub = "boolne";
}
/* Choose the right opcode */
if (RegValIsKnown (E->RI->In.RegA) && E->RI->In.RegA == 0) {
Op = OP65_CPX;
} else if (RegValIsKnown (E->RI->In.RegX) && E->RI->In.RegX == 0) {
Op = OP65_CMP;
}
/* Replace the sequence if all requirements are met*/
if (Op != OP65_COUNT && Sub != 0) {
/* Replace bcastax/bnegax with boolne/booleq */
CE_SetArg (E, Sub);
/* Insert the compare */
X = NewCodeEntry (Op, AM65_IMM, "$00", 0, E->LI);
CS_InsertEntry (S, X, I);
/* Remember, we had changes */
++Changes;
/* Correct the index */
++I;
}
/* Reset the choices */
Op = OP65_COUNT;
Sub = 0;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* bnega optimizations */
/*****************************************************************************/
unsigned OptBNegA1 (CodeSeg* S)
/* Check for
**
** ldx #$00
** lda ..
** jsr bnega
**
** Remove the ldx if the lda does not use it.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for a ldx */
if (E->OPC == OP65_LDX &&
E->AM == AM65_IMM &&
CE_HasNumArg (E) &&
E->Num == 0 &&
CS_GetEntries (S, L, I+1, 2) &&
L[0]->OPC == OP65_LDA &&
(L[0]->Use & REG_X) == 0 &&
!CE_HasLabel (L[0]) &&
CE_IsCallTo (L[1], "bnega") &&
!CE_HasLabel (L[1])) {
/* Remove the ldx instruction */
CS_DelEntry (S, I);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegA2 (CodeSeg* S)
/* Check for
**
** lda ..
** jsr bnega
** jeq/jne ..
**
** Adjust the conditional branch and remove the call to the subroutine.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if ((E->OPC == OP65_ADC ||
E->OPC == OP65_AND ||
E->OPC == OP65_DEA ||
E->OPC == OP65_EOR ||
E->OPC == OP65_INA ||
E->OPC == OP65_LDA ||
E->OPC == OP65_ORA ||
E->OPC == OP65_PLA ||
E->OPC == OP65_SBC ||
E->OPC == OP65_TXA ||
E->OPC == OP65_TYA) &&
CS_GetEntries (S, L, I+1, 2) &&
CE_IsCallTo (L[0], "bnega") &&
!CE_HasLabel (L[0]) &&
(L[1]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[1]) &&
(GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) {
/* Invert the branch */
CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
/* Delete the subroutine call */
CS_DelEntry (S, I+1);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* bnegax optimizations */
/*****************************************************************************/
unsigned OptBNegAX1 (CodeSeg* S)
/* On a call to bnegax, if X is zero, the result depends only on the value in
** A, so change the call to a call to bnega. This will get further optimized
** later if possible.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to bnegax, and if X is known and zero */
if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) {
CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI);
CS_InsertEntry (S, X, I+1);
CS_DelEntry (S, I);
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX2 (CodeSeg* S)
/* Search for the sequence:
**
** ldy #xx
** jsr ldaxysp
** jsr bnegax
** jne/jeq ...
**
** and replace it by
**
** ldy #xx
** lda (sp),y
** dey
** ora (sp),y
** jeq/jne ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[4];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
if (L[0]->OPC == OP65_LDY &&
CE_IsConstImm (L[0]) &&
!CS_RangeHasLabel (S, I+1, 3) &&
CS_GetEntries (S, L+1, I+1, 3) &&
CE_IsCallTo (L[1], "ldaxysp") &&
CE_IsCallTo (L[2], "bnegax") &&
(L[3]->Info & OF_ZBRA) != 0 &&
(GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) {
CodeEntry* X;
/* lda (sp),y */
X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
CS_InsertEntry (S, X, I+1);
/* dey */
X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI);
CS_InsertEntry (S, X, I+2);
/* ora (sp),y */
X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
CS_InsertEntry (S, X, I+3);
/* Invert the branch */
CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC));
/* Delete the entries no longer needed. */
CS_DelEntries (S, I+4, 2);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX3 (CodeSeg* S)
/* Search for the sequence:
**
** lda xx
** ldx yy
** jsr bnegax
** jne/jeq ...
**
** and replace it by
**
** lda xx
** ora xx+1
** jeq/jne ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[3];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_LDA &&
CS_GetEntries (S, L, I+1, 3) &&
L[0]->OPC == OP65_LDX &&
!CE_HasLabel (L[0]) &&
CE_IsCallTo (L[1], "bnegax") &&
!CE_HasLabel (L[1]) &&
(L[2]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[2]) &&
(GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) {
/* ldx --> ora */
CE_ReplaceOPC (L[0], OP65_ORA);
/* Invert the branch */
CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
/* Delete the subroutine call */
CS_DelEntry (S, I+2);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX4 (CodeSeg* S)
/* Search for the sequence:
**
** jsr xxx
** jsr bnega(x)
** jeq/jne ...
**
** and replace it by:
**
** jsr xxx
** <boolean test>
** jne/jeq ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_JSR &&
CS_GetEntries (S, L, I+1, 2) &&
L[0]->OPC == OP65_JSR &&
strncmp (L[0]->Arg,"bnega",5) == 0 &&
!CE_HasLabel (L[0]) &&
(L[1]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[1]) &&
(GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) {
CodeEntry* X;
/* Check if we're calling bnega or bnegax */
int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
/* Insert apropriate test code */
if (ByteSized) {
/* Test bytes */
X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
CS_InsertEntry (S, X, I+2);
} else {
/* Test words */
X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
CS_InsertEntry (S, X, I+2);
X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
CS_InsertEntry (S, X, I+3);
}
/* Delete the subroutine call */
CS_DelEntry (S, I+1);
/* Invert the branch */
CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}

View File

@ -1,8 +1,8 @@
/*****************************************************************************/
/* */
/* coptneg.h */
/* coptbool.h */
/* */
/* Optimize negation sequences */
/* Optimize boolean sequences */
/* */
/* */
/* */
@ -33,8 +33,8 @@
#ifndef COPTNEG_H
#define COPTNEG_H
#ifndef COPTBOOL_H
#define COPTBOOL_H
@ -43,6 +43,56 @@
/*****************************************************************************/
/* Optimize bool comparison and transformer subroutines */
/*****************************************************************************/
unsigned OptBoolCmp (CodeSeg* S);
/* Search for calls to compare subroutines followed by a conditional branch
** and replace them by cheaper versions, since the branch means that the
** boolean value returned by these routines is not needed (we may also check
** that explicitly, but for the current code generator it is always true).
*/
unsigned OptBoolTrans (CodeSeg* S);
/* Try to remove the call to boolean transformer routines where the call is
** not really needed and change following branch condition accordingly.
*/
/*****************************************************************************/
/* Remove calls to the boolean cast/negation subroutines */
/*****************************************************************************/
unsigned OptBoolUnary1 (CodeSeg* S);
/* Search for and remove bcastax adjacent to bnegax */
unsigned OptBoolUnary2 (CodeSeg* S);
/* Search for and remove bcastax/bnegax following a boolean transformer.
** Invert the boolean transformer if it is bnegax to be removed.
*/
unsigned OptBoolUnary3 (CodeSeg* S);
/* Replace bcastax/bnegax with
**
** cpx #0
** jsr boolne/booleq
**
** if A == 0, or replace bcastax/bnegax with
**
** cmp #0
** jsr boolne/booleq
**
** if X == 0.
*/
/*****************************************************************************/
/* bnega optimizations */
/*****************************************************************************/
@ -132,54 +182,6 @@ unsigned OptBNegAX4 (CodeSeg* S);
/*****************************************************************************/
/* negax optimizations */
/*****************************************************************************/
unsigned OptNegAX1 (CodeSeg* S);
/* Search for a call to negax and replace it by
**
** eor #$FF
** clc
** adc #$01
**
** if X isn't used later.
*/
unsigned OptNegAX2 (CodeSeg* S);
/* Search for a call to negax and replace it by
**
** ldx #$FF
** eor #$FF
** clc
** adc #$01
** bcc L1
** inx
** L1:
**
** if X is known and zero on entry.
*/
/*****************************************************************************/
/* complax optimizations */
/*****************************************************************************/
unsigned OptComplAX1 (CodeSeg* S);
/* Search for a call to complax and replace it by
**
** eor #$FF
**
** if X isn't used later.
*/
/* End of coptneg.h */
/* End of coptbool.h */
#endif

View File

@ -43,131 +43,12 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Table used to invert a condition, indexed by condition */
static const unsigned char CmpInvertTab [] = {
CMP_NE, CMP_EQ,
CMP_LE, CMP_LT, CMP_GE, CMP_GT,
CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
};
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond)
/* Helper function for the replacement of routines that return a boolean
** followed by a conditional jump. Instead of the boolean value, the condition
** codes are evaluated directly.
** I is the index of the conditional branch, the sequence is already checked
** to be correct.
*/
{
CodeEntry* N;
CodeLabel* L;
/* Get the entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Replace the conditional branch */
switch (Cond) {
case CMP_EQ:
CE_ReplaceOPC (E, OP65_JEQ);
break;
case CMP_NE:
CE_ReplaceOPC (E, OP65_JNE);
break;
case CMP_GT:
/* Replace by
** beq @L
** jpl Target
** @L: ...
*/
if ((N = CS_GetNextEntry (S, I)) == 0) {
/* No such entry */
Internal ("Invalid program flow");
}
L = CS_GenLabel (S, N);
N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I);
CE_ReplaceOPC (E, OP65_JPL);
break;
case CMP_GE:
CE_ReplaceOPC (E, OP65_JPL);
break;
case CMP_LT:
CE_ReplaceOPC (E, OP65_JMI);
break;
case CMP_LE:
/* Replace by
** jmi Target
** jeq Target
*/
CE_ReplaceOPC (E, OP65_JMI);
L = E->JumpTo;
N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I+1);
break;
case CMP_UGT:
/* Replace by
** beq @L
** jcs Target
** @L: ...
*/
if ((N = CS_GetNextEntry (S, I)) == 0) {
/* No such entry */
Internal ("Invalid program flow");
}
L = CS_GenLabel (S, N);
N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I);
CE_ReplaceOPC (E, OP65_JCS);
break;
case CMP_UGE:
CE_ReplaceOPC (E, OP65_JCS);
break;
case CMP_ULT:
CE_ReplaceOPC (E, OP65_JCC);
break;
case CMP_ULE:
/* Replace by
** jcc Target
** jeq Target
*/
CE_ReplaceOPC (E, OP65_JCC);
L = E->JumpTo;
N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, N, I+1);
break;
default:
Internal ("Unknown jump condition: %d", Cond);
}
}
static int IsImmCmp16 (CodeEntry** L)
/* Check if the instructions at L are an immediate compare of a/x:
**
@ -205,68 +86,6 @@ static int GetCmpRegVal (const CodeEntry* E)
/*****************************************************************************/
/* Remove calls to the bool transformer subroutines */
/*****************************************************************************/
unsigned OptBoolTrans (CodeSeg* S)
/* Try to remove the call to boolean transformer routines where the call is
** not really needed.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
cmp_t Cond;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for a boolean transformer */
if (E->OPC == OP65_JSR &&
(Cond = FindBoolCmpCond (E->Arg)) != CMP_INV &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_ZBRA) != 0) {
/* Make the boolean transformer unnecessary by changing the
** the conditional jump to evaluate the condition flags that
** are set after the compare directly. Note: jeq jumps if
** the condition is not met, jne jumps if the condition is met.
** Invert the code if we jump on condition not met.
*/
if (GetBranchCond (N->OPC) == BC_EQ) {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* Check if we can replace the code by something better */
ReplaceCmp (S, I+1, Cond);
/* Remove the call to the bool transformer */
CS_DelEntry (S, I);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* Optimizations for compares */
/*****************************************************************************/
@ -684,68 +503,6 @@ unsigned OptCmp5 (CodeSeg* S)
unsigned OptCmp6 (CodeSeg* S)
/* Search for calls to compare subroutines followed by a conditional branch
** and replace them by cheaper versions, since the branch means that the
** boolean value returned by these routines is not needed (we may also check
** that explicitly, but for the current code generator it is always true).
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
cmp_t Cond;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_JSR &&
(Cond = FindTosCmpCond (E->Arg)) != CMP_INV &&
(N = CS_GetNextEntry (S, I)) != 0 &&
(N->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (N)) {
/* The tos... functions will return a boolean value in a/x and
** the Z flag says if this value is zero or not. We will call
** a cheaper subroutine instead, one that does not return a
** boolean value but only valid flags. Note: jeq jumps if
** the condition is not met, jne jumps if the condition is met.
** Invert the code if we jump on condition not met.
*/
if (GetBranchCond (N->OPC) == BC_EQ) {
/* Jumps if condition false, invert condition */
Cond = CmpInvertTab [Cond];
}
/* Replace the subroutine call. */
E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI);
CS_InsertEntry (S, E, I+1);
CS_DelEntry (S, I);
/* Replace the conditional branch */
ReplaceCmp (S, I+1, Cond);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptCmp7 (CodeSeg* S)
/* Search for a sequence ldx/txa/branch and remove the txa if A is not
** used later.

View File

@ -43,19 +43,6 @@
/*****************************************************************************/
/* Remove calls to the bool transformer subroutines */
/*****************************************************************************/
unsigned OptBoolTrans (CodeSeg* S);
/* Try to remove the call to boolean transformer routines where the call is
** not really needed.
*/
/*****************************************************************************/
/* Optimizations for compares */
/*****************************************************************************/
@ -136,13 +123,6 @@ unsigned OptCmp5 (CodeSeg* S);
** jne/jeq L2
*/
unsigned OptCmp6 (CodeSeg* S);
/* Search for calls to compare subroutines followed by a conditional branch
** and replace them by cheaper versions, since the branch means that the
** boolean value returned by these routines is not needed (we may also check
** that explicitly, but for the current code generator it is always true).
*/
unsigned OptCmp7 (CodeSeg* S);
/* Search for a sequence ldx/txa/branch and remove the txa if A is not
** used later.

View File

@ -898,17 +898,13 @@ unsigned OptJumpTarget3 (CodeSeg* S)
unsigned OptCondBranches1 (CodeSeg* S)
/* Performs several optimization steps:
**
unsigned OptCondBranch1 (CodeSeg* S)
/* Performs some optimization steps:
** - If an immediate 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
** manner, remove the conditional branch.
** - If the conditional branch is always taken because of the register load,
** replace it by a jmp.
** - If a conditional branch jumps around an unconditional branch, remove the
** conditional branch and make the jump a conditional branch with the
** inverse condition of the first one.
*/
{
unsigned Changes = 0;
@ -918,7 +914,6 @@ unsigned OptCondBranches1 (CodeSeg* S)
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
CodeLabel* L;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
@ -960,6 +955,35 @@ unsigned OptCondBranches1 (CodeSeg* S)
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptCondBranch2 (CodeSeg* S)
/* If a conditional branch jumps around an unconditional branch, remove the
** conditional branch and make the jump a conditional branch with the inverse
** condition of the first one.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* N;
CodeLabel* L;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */
(L = E->JumpTo) != 0 && /* ..referencing a local label */
(N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */
@ -991,7 +1015,51 @@ unsigned OptCondBranches1 (CodeSeg* S)
unsigned OptCondBranches2 (CodeSeg* S)
unsigned OptCondBranch3 (CodeSeg* S)
/* If the conditional branch is always taken because it follows an inverse
** conditional branch, replace it by a jmp.
*/
{
unsigned Changes = 0;
/* 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 conditional branch */
if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */
(N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */
(N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */
!CE_HasLabel (N)) { /* ..and does not have a label */
/* Check if the branches conditions are inverse of each other */
if (GetInverseCond (GetBranchCond (N->OPC)) == GetBranchCond (E->OPC)) {
/* The branch is always taken, replace it by a jump */
CE_ReplaceOPC (N, OP65_JMP);
/* Remember, we had changes */
++Changes;
}
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptCondBranchC (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 flag.
*/

View File

@ -101,13 +101,27 @@ unsigned OptJumpTarget3 (CodeSeg* S);
** done.
*/
unsigned OptCondBranches1 (CodeSeg* S);
/* 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
** manner, remove the conditional branch.
unsigned OptCondBranch1 (CodeSeg* S);
/* Performs some optimization steps:
** - If an immediate 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
** manner, remove the conditional branch.
** - If the conditional branch is always taken because of the register load,
** replace it by a jmp.
*/
unsigned OptCondBranches2 (CodeSeg* S);
unsigned OptCondBranch2 (CodeSeg* S);
/* If a conditional branch jumps around an unconditional branch, remove the
** conditional branch and make the jump a conditional branch with the inverse
** condition of the first one.
*/
unsigned OptCondBranch3 (CodeSeg* S);
/* If the conditional branch is always taken because it follows an inverse
** conditional branch, replace it by a jmp.
*/
unsigned OptCondBranchC (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.
*/

View File

@ -1,607 +0,0 @@
/*****************************************************************************/
/* */
/* coptneg.c */
/* */
/* Optimize negation sequences */
/* */
/* */
/* */
/* (C) 2001-2012, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* 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. */
/* */
/*****************************************************************************/
/* cc65 */
#include "codeent.h"
#include "codeinfo.h"
#include "coptneg.h"
/*****************************************************************************/
/* bnega optimizations */
/*****************************************************************************/
unsigned OptBNegA1 (CodeSeg* S)
/* Check for
**
** ldx #$00
** lda ..
** jsr bnega
**
** Remove the ldx if the lda does not use it.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for a ldx */
if (E->OPC == OP65_LDX &&
E->AM == AM65_IMM &&
CE_HasNumArg (E) &&
E->Num == 0 &&
CS_GetEntries (S, L, I+1, 2) &&
L[0]->OPC == OP65_LDA &&
(L[0]->Use & REG_X) == 0 &&
!CE_HasLabel (L[0]) &&
CE_IsCallTo (L[1], "bnega") &&
!CE_HasLabel (L[1])) {
/* Remove the ldx instruction */
CS_DelEntry (S, I);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegA2 (CodeSeg* S)
/* Check for
**
** lda ..
** jsr bnega
** jeq/jne ..
**
** Adjust the conditional branch and remove the call to the subroutine.
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if ((E->OPC == OP65_ADC ||
E->OPC == OP65_AND ||
E->OPC == OP65_DEA ||
E->OPC == OP65_EOR ||
E->OPC == OP65_INA ||
E->OPC == OP65_LDA ||
E->OPC == OP65_ORA ||
E->OPC == OP65_PLA ||
E->OPC == OP65_SBC ||
E->OPC == OP65_TXA ||
E->OPC == OP65_TYA) &&
CS_GetEntries (S, L, I+1, 2) &&
CE_IsCallTo (L[0], "bnega") &&
!CE_HasLabel (L[0]) &&
(L[1]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[1])) {
/* Invert the branch */
CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
/* Delete the subroutine call */
CS_DelEntry (S, I+1);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* bnegax optimizations */
/*****************************************************************************/
unsigned OptBNegAX1 (CodeSeg* S)
/* On a call to bnegax, if X is zero, the result depends only on the value in
** A, so change the call to a call to bnega. This will get further optimized
** later if possible.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to bnegax, and if X is known and zero */
if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) {
CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI);
CS_InsertEntry (S, X, I+1);
CS_DelEntry (S, I);
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX2 (CodeSeg* S)
/* Search for the sequence:
**
** ldy #xx
** jsr ldaxysp
** jsr bnegax
** jne/jeq ...
**
** and replace it by
**
** ldy #xx
** lda (sp),y
** dey
** ora (sp),y
** jeq/jne ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[4];
/* Get next entry */
L[0] = CS_GetEntry (S, I);
/* Check for the sequence */
if (L[0]->OPC == OP65_LDY &&
CE_IsConstImm (L[0]) &&
!CS_RangeHasLabel (S, I+1, 3) &&
CS_GetEntries (S, L+1, I+1, 3) &&
CE_IsCallTo (L[1], "ldaxysp") &&
CE_IsCallTo (L[2], "bnegax") &&
(L[3]->Info & OF_ZBRA) != 0) {
CodeEntry* X;
/* lda (sp),y */
X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
CS_InsertEntry (S, X, I+1);
/* dey */
X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI);
CS_InsertEntry (S, X, I+2);
/* ora (sp),y */
X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
CS_InsertEntry (S, X, I+3);
/* Invert the branch */
CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC));
/* Delete the entries no longer needed. */
CS_DelEntries (S, I+4, 2);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX3 (CodeSeg* S)
/* Search for the sequence:
**
** lda xx
** ldx yy
** jsr bnegax
** jne/jeq ...
**
** and replace it by
**
** lda xx
** ora xx+1
** jeq/jne ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[3];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_LDA &&
CS_GetEntries (S, L, I+1, 3) &&
L[0]->OPC == OP65_LDX &&
!CE_HasLabel (L[0]) &&
CE_IsCallTo (L[1], "bnegax") &&
!CE_HasLabel (L[1]) &&
(L[2]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[2])) {
/* ldx --> ora */
CE_ReplaceOPC (L[0], OP65_ORA);
/* Invert the branch */
CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC));
/* Delete the subroutine call */
CS_DelEntry (S, I+2);
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptBNegAX4 (CodeSeg* S)
/* Search for the sequence:
**
** jsr xxx
** jsr bnega(x)
** jeq/jne ...
**
** and replace it by:
**
** jsr xxx
** <boolean test>
** jne/jeq ...
*/
{
unsigned Changes = 0;
/* Walk over the entries */
unsigned I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* L[2];
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check for the sequence */
if (E->OPC == OP65_JSR &&
CS_GetEntries (S, L, I+1, 2) &&
L[0]->OPC == OP65_JSR &&
strncmp (L[0]->Arg,"bnega",5) == 0 &&
!CE_HasLabel (L[0]) &&
(L[1]->Info & OF_ZBRA) != 0 &&
!CE_HasLabel (L[1])) {
CodeEntry* X;
/* Check if we're calling bnega or bnegax */
int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0);
/* Insert apropriate test code */
if (ByteSized) {
/* Test bytes */
X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI);
CS_InsertEntry (S, X, I+2);
} else {
/* Test words */
X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI);
CS_InsertEntry (S, X, I+2);
X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI);
CS_InsertEntry (S, X, I+3);
}
/* Delete the subroutine call */
CS_DelEntry (S, I+1);
/* Invert the branch */
CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC));
/* Remember, we had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* negax optimizations */
/*****************************************************************************/
unsigned OptNegAX1 (CodeSeg* S)
/* Search for a call to negax and replace it by
**
** eor #$FF
** clc
** adc #$01
**
** if X isn't used later.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X isn't used later */
if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) {
CodeEntry* X;
/* Add replacement code behind */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+2);
X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI);
CS_InsertEntry (S, X, I+3);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* Skip the generated code */
I += 2;
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptNegAX2 (CodeSeg* S)
/* Search for a call to negax and replace it by
**
** ldx #$FF
** eor #$FF
** clc
** adc #$01
** bcc L1
** inx
** L1:
**
** if X is known and zero on entry.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* P;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X is known and zero */
if (E->RI->In.RegX == 0 &&
CE_IsCallTo (E, "negax") &&
(P = CS_GetNextEntry (S, I)) != 0) {
CodeEntry* X;
CodeLabel* L;
/* Add replacement code behind */
/* ldx #$FF */
X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
/* eor #$FF */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+2);
/* clc */
X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+3);
/* adc #$01 */
X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI);
CS_InsertEntry (S, X, I+4);
/* Get the label attached to the insn following the call */
L = CS_GenLabel (S, P);
/* bcc L */
X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, X, I+5);
/* inx */
X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+6);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* Skip the generated code */
I += 5;
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* complax optimizations */
/*****************************************************************************/
unsigned OptComplAX1 (CodeSeg* S)
/* Search for a call to complax and replace it by
**
** eor #$FF
**
** if X isn't used later.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X isn't used later */
if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) {
CodeEntry* X;
/* Add replacement code behind */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}

236
src/cc65/coptunary.c Normal file
View File

@ -0,0 +1,236 @@
/*****************************************************************************/
/* */
/* coptunary.c */
/* */
/* Optimize bitwise unary sequences */
/* */
/* */
/* */
/* (C) 2001-2012, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* 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. */
/* */
/*****************************************************************************/
/* cc65 */
#include "codeent.h"
#include "codeinfo.h"
#include "coptbool.h"
/*****************************************************************************/
/* negax optimizations */
/*****************************************************************************/
unsigned OptNegAX1 (CodeSeg* S)
/* Search for a call to negax and replace it by
**
** eor #$FF
** clc
** adc #$01
**
** if X isn't used later.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X isn't used later */
if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) {
CodeEntry* X;
/* Add replacement code behind */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+2);
X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI);
CS_InsertEntry (S, X, I+3);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* Skip the generated code */
I += 2;
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
unsigned OptNegAX2 (CodeSeg* S)
/* Search for a call to negax and replace it by
**
** ldx #$FF
** eor #$FF
** clc
** adc #$01
** bcc L1
** inx
** L1:
**
** if X is known and zero on entry.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
CodeEntry* P;
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X is known and zero */
if (E->RI->In.RegX == 0 &&
CE_IsCallTo (E, "negax") &&
(P = CS_GetNextEntry (S, I)) != 0) {
CodeEntry* X;
CodeLabel* L;
/* Add replacement code behind */
/* ldx #$FF */
X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
/* eor #$FF */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+2);
/* clc */
X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+3);
/* adc #$01 */
X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI);
CS_InsertEntry (S, X, I+4);
/* Get the label attached to the insn following the call */
L = CS_GenLabel (S, P);
/* bcc L */
X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI);
CS_InsertEntry (S, X, I+5);
/* inx */
X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI);
CS_InsertEntry (S, X, I+6);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* Skip the generated code */
I += 5;
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}
/*****************************************************************************/
/* complax optimizations */
/*****************************************************************************/
unsigned OptComplAX1 (CodeSeg* S)
/* Search for a call to complax and replace it by
**
** eor #$FF
**
** if X isn't used later.
*/
{
unsigned Changes = 0;
unsigned I;
/* Walk over the entries */
I = 0;
while (I < CS_GetEntryCount (S)) {
/* Get next entry */
CodeEntry* E = CS_GetEntry (S, I);
/* Check if this is a call to negax, and if X isn't used later */
if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) {
CodeEntry* X;
/* Add replacement code behind */
X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI);
CS_InsertEntry (S, X, I+1);
/* Delete the call to negax */
CS_DelEntry (S, I);
/* We had changes */
++Changes;
}
/* Next entry */
++I;
}
/* Return the number of changes made */
return Changes;
}

96
src/cc65/coptunary.h Normal file
View File

@ -0,0 +1,96 @@
/*****************************************************************************/
/* */
/* coptunary.h */
/* */
/* Optimize bitwise unary sequences */
/* */
/* */
/* */
/* (C) 2001-2012, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* 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 COPTUNARY_H
#define COPTUNARY_H
/* cc65 */
#include "codeseg.h"
/*****************************************************************************/
/* negax optimizations */
/*****************************************************************************/
unsigned OptNegAX1 (CodeSeg* S);
/* Search for a call to negax and replace it by
**
** eor #$FF
** clc
** adc #$01
**
** if X isn't used later.
*/
unsigned OptNegAX2 (CodeSeg* S);
/* Search for a call to negax and replace it by
**
** ldx #$FF
** eor #$FF
** clc
** adc #$01
** bcc L1
** inx
** L1:
**
** if X is known and zero on entry.
*/
/*****************************************************************************/
/* complax optimizations */
/*****************************************************************************/
unsigned OptComplAX1 (CodeSeg* S);
/* Search for a call to complax and replace it by
**
** eor #$FF
**
** if X isn't used later.
*/
/* End of coptunary.h */
#endif

View File

@ -1350,8 +1350,6 @@ static void Primary (ExprDesc* E)
/* String literal */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
E->V.LVal = UseLiteral (CurTok.SVal);
/* Translate into target charset */
TranslateLiteral (E->V.LVal);
} else {
E->V.LVal = CurTok.SVal;
}
@ -3477,8 +3475,9 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef)
}
LOG(("%s:%d AddDone:%d\n", __FILE__, __LINE__, AddDone));
if (!AddDone) {
if (ED_IsLocQuasiConst (&Expr2) &&
rscale == 1 &&
if (ED_IsLocQuasiConst (&Expr2) &&
!IsTypeBitField (Expr2.Type) &&
rscale == 1 &&
CheckedSizeOf (rhst) == SIZEOF_CHAR) {
LOG(("%s:%d char\n", __FILE__, __LINE__));
/* Change the order back */

View File

@ -267,9 +267,6 @@ static void DefineBitFieldData (StructInitData* SI)
static void DefineStrData (Literal* Lit, unsigned Count)
{
/* Translate into target charset */
TranslateLiteral (Lit);
/* Output the data */
g_defbytes (GetLiteralStr (Lit), Count);
}

View File

@ -160,13 +160,24 @@ void ReleaseLiteral (Literal* L)
void TranslateLiteral (Literal* L)
/* Translate a literal into the target charset. */
/* Translate a literal into the target charset */
{
TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data));
}
void ConcatLiteral (Literal* L, const Literal* Appended)
/* Concatenate string literals */
{
if (SB_GetLen (&L->Data) > 0 && SB_LookAtLast (&L->Data) == '\0') {
SB_Drop (&L->Data, 1);
}
SB_Append (&L->Data, &Appended->Data);
}
unsigned GetLiteralLabel (const Literal* L)
/* Return the asm label for a literal */
{

View File

@ -75,7 +75,10 @@ void ReleaseLiteral (Literal* L);
/* Decrement the reference counter for the literal */
void TranslateLiteral (Literal* L);
/* Translate a literal into the target charset. */
/* Translate a literal into the target charset */
void ConcatLiteral (Literal* L, const Literal* Appended);
/* Concatenate string literals */
unsigned GetLiteralLabel (const Literal* L);
/* Return the asm label for a literal */

View File

@ -41,12 +41,11 @@
#include "chartype.h"
#include "segnames.h"
#include "tgttrans.h"
#include "xmalloc.h"
/* cc65 */
#include "codegen.h"
#include "error.h"
#include "expr.h"
#include "funcdesc.h"
#include "global.h"
#include "litpool.h"
#include "scanner.h"
@ -58,7 +57,7 @@
/*****************************************************************************/
/* data */
/* Data */
/*****************************************************************************/
@ -142,6 +141,21 @@ typedef enum {
PP_ERROR,
} PushPopResult;
/* Effective scope of the pragma.
** This talks about how far the pragma has effects on whenever it shows up,
** even in the middle of an expression, statement or something.
*/
typedef enum {
PES_NONE,
PES_IMM, /* No way back */
PES_EXPR, /* Current expression/declarator */
PES_STMT, /* Current statement/declaration */
PES_SCOPE, /* Current scope */
PES_FUNC, /* Current function */
PES_FILE, /* Current file */
PES_ALL, /* All */
} pragma_scope_t;
/*****************************************************************************/
@ -339,7 +353,7 @@ static void PushInt (IntStack* S, long Val)
static int BoolKeyword (StrBuf* Ident)
static int IsBoolKeyword (StrBuf* Ident)
/* Check if the identifier in Ident is a keyword for a boolean value. Currently
** accepted are true/false/on/off.
*/
@ -364,17 +378,92 @@ static int BoolKeyword (StrBuf* Ident)
static void ApplyPragma (int PushPop, IntStack* Stack, long Val)
/* Apply a pragma immediately */
{
if (PushPop > 0) {
/* Push the new value */
PushInt (Stack, Val);
} else if (PushPop < 0) {
/* Pop the old value */
PopInt (Stack);
} else {
/* Set the new value */
IS_Set (Stack, Val);
}
}
static void ApplySegNamePragma (pragma_t Token, int PushPop, const char* Name, unsigned char AddrSize)
/* Process a segname pragma */
{
segment_t Seg = SEG_CODE;
switch (Token) {
case PRAGMA_CODE_NAME:
case PRAGMA_CODESEG:
Seg = SEG_CODE;
break;
case PRAGMA_RODATA_NAME:
case PRAGMA_RODATASEG:
Seg = SEG_RODATA;
break;
case PRAGMA_DATA_NAME:
case PRAGMA_DATASEG:
Seg = SEG_DATA;
break;
case PRAGMA_BSS_NAME:
case PRAGMA_BSSSEG:
Seg = SEG_BSS;
break;
default:
Internal ("Unknown segment name pragma: %02X", Token);
break;
}
/* Set the new name */
if (PushPop > 0) {
PushSegName (Seg, Name);
} else if (PushPop < 0) {
PopSegName (Seg);
} else {
SetSegName (Seg, Name);
}
/* Set the optional address size for the segment if valid */
if (PushPop >= 0 && AddrSize != ADDR_SIZE_INVALID) {
SetSegAddrSize (Name, AddrSize);
}
/* BSS variables are output at the end of the compilation. Don't
** bother to change their segment, now.
*/
if (Seg != SEG_BSS) {
g_segname (Seg);
}
}
/*****************************************************************************/
/* Pragma handling functions */
/*****************************************************************************/
static void StringPragma (StrBuf* B, void (*Func) (const char*))
static void StringPragma (pragma_scope_t Scope, StrBuf* B, void (*Func) (const char*))
/* Handle a pragma that expects a string parameter */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* We expect a string here */
if (GetString (B, &S)) {
/* Call the given function with the string argument */
@ -387,14 +476,17 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*))
static void SegNamePragma (StrBuf* B, segment_t Seg)
static void SegNamePragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B)
/* Handle a pragma that expects a segment name parameter */
{
const char* Name;
unsigned char AddrSize = ADDR_SIZE_INVALID;
StrBuf S = AUTO_STRBUF_INITIALIZER;
StrBuf A = AUTO_STRBUF_INITIALIZER;
int Push = 0;
int PushPop = 0;
/* Unused at the moment */
(void)Scope;
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
@ -403,19 +495,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
break;
case PP_PUSH:
Push = 1;
PushPop = 1;
break;
case PP_POP:
/* Pop the old value and output it */
PopSegName (Seg);
/* BSS variables are output at the end of the compilation. Don't
** bother to change their segment, now.
*/
if (Seg != SEG_BSS) {
g_segname (Seg);
}
ApplySegNamePragma (Token, -1, 0, 0);
/* Done */
goto ExitPoint;
@ -454,27 +539,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
/* Get the address size for the segment */
AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A));
/* Set the address size for the segment if valid */
if (AddrSize != ADDR_SIZE_INVALID) {
SetSegAddrSize (Name, AddrSize);
} else {
Warning ("Invalid address size for segment!");
/* Check the address size for the segment */
if (AddrSize == ADDR_SIZE_INVALID) {
Warning ("Invalid address size for segment");
}
}
/* Set the new name and optionally address size */
if (Push) {
PushSegName (Seg, Name);
} else {
SetSegName (Seg, Name);
}
/* BSS variables are output at the end of the compilation. Don't
** bother to change their segment, now.
*/
if (Seg != SEG_BSS) {
g_segname (Seg);
}
ApplySegNamePragma (Token, PushPop, Name, AddrSize);
} else {
@ -484,13 +556,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
}
ExitPoint:
/* Call the string buf destructor */
SB_Done (&S);
SB_Done (&A);
}
static void WrappedCallPragma (StrBuf* B)
static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B)
/* Handle the wrapped-call pragma */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
@ -498,6 +572,9 @@ static void WrappedCallPragma (StrBuf* B)
long Val;
SymEntry *Entry;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
@ -573,11 +650,14 @@ ExitPoint:
static void CharMapPragma (StrBuf* B)
static void CharMapPragma (pragma_scope_t Scope, StrBuf* B)
/* Change the character map */
{
long Index, C;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Read the character index */
if (!GetNumber (B, &Index)) {
return;
@ -619,7 +699,7 @@ static void CharMapPragma (StrBuf* B)
static void WarnPragma (StrBuf* B)
static void WarnPragma (pragma_scope_t Scope, StrBuf* B)
/* Enable/disable warnings */
{
long Val;
@ -627,6 +707,10 @@ static void WarnPragma (StrBuf* B)
/* A warning name must follow */
IntStack* S = GetWarning (B);
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
if (S == 0) {
return;
}
@ -680,48 +764,47 @@ static void WarnPragma (StrBuf* B)
static void FlagPragma (StrBuf* B, IntStack* Stack)
static void FlagPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack)
/* Handle a pragma that expects a boolean parameter */
{
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
long Val;
int Push;
int PushPop = 0;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* Try to read an identifier */
int IsIdent = SB_GetSym (B, &Ident, 0);
/* Check if we have a first argument named "pop" */
if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
PopInt (Stack);
/* Pop the old value and bail out */
ApplyPragma (-1, Stack, 0);
/* No other arguments allowed */
return;
}
/* Check if we have a first argument named "push" */
if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
Push = 1;
PushPop = 1;
if (!GetComma (B)) {
goto ExitPoint;
}
IsIdent = SB_GetSym (B, &Ident, 0);
} else {
Push = 0;
}
/* Boolean argument follows */
if (IsIdent) {
Val = BoolKeyword (&Ident);
Val = IsBoolKeyword (&Ident);
} else if (!GetNumber (B, &Val)) {
goto ExitPoint;
}
/* Set/push the new value */
if (Push) {
PushInt (Stack, Val);
} else {
IS_Set (Stack, Val);
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (PushPop, Stack, Val);
ExitPoint:
/* Free the identifier */
@ -730,12 +813,16 @@ ExitPoint:
static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
static void IntPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack, long Low, long High)
/* Handle a pragma that expects an int parameter */
{
long Val;
int Push;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
@ -749,7 +836,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
case PP_POP:
/* Pop the old value and bail out */
PopInt (Stack);
ApplyPragma (-1, Stack, 0);
return;
case PP_ERROR:
@ -772,31 +859,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
return;
}
/* Set/push the new value */
if (Push) {
PushInt (Stack, Val);
} else {
IS_Set (Stack, Val);
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (Push, Stack, Val);
}
static void MakeMessage (const char* Message)
static void NoteMessagePragma (const char* Message)
/* Wrapper for printf-like Note() function protected from user-provided format
** specifiers.
*/
{
Note ("%s", Message);
}
static void ParsePragma (void)
/* Parse the contents of the _Pragma statement */
static void ParsePragmaString (void)
/* Parse the contents of _Pragma */
{
pragma_t Pragma;
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
/* Create a string buffer from the string literal */
StrBuf B = AUTO_STRBUF_INITIALIZER;
SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
/* Skip the string token */
@ -837,111 +925,130 @@ static void ParsePragma (void)
switch (Pragma) {
case PRAGMA_ALIGN:
IntPragma (&B, &DataAlignment, 1, 4096);
/* TODO: PES_EXPR (PES_DECL) */
IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096);
break;
case PRAGMA_ALLOW_EAGER_INLINE:
FlagPragma (&B, &EagerlyInlineFuncs);
FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs);
break;
case PRAGMA_BSSSEG:
Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
/* FALLTHROUGH */
case PRAGMA_BSS_NAME:
SegNamePragma (&B, SEG_BSS);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B);
break;
case PRAGMA_CHARMAP:
CharMapPragma (&B);
CharMapPragma (PES_IMM, &B);
break;
case PRAGMA_CHECKSTACK:
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
/* FALLTHROUGH */
case PRAGMA_CHECK_STACK:
FlagPragma (&B, &CheckStack);
/* TODO: PES_SCOPE maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &CheckStack);
break;
case PRAGMA_CODESEG:
Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
/* FALLTHROUGH */
case PRAGMA_CODE_NAME:
SegNamePragma (&B, SEG_CODE);
/* PES_FUNC is the only sensible option so far */
SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B);
break;
case PRAGMA_CODESIZE:
IntPragma (&B, &CodeSizeFactor, 10, 1000);
/* PES_EXPR would be optimization nightmare */
IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000);
break;
case PRAGMA_DATASEG:
Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
/* FALLTHROUGH */
case PRAGMA_DATA_NAME:
SegNamePragma (&B, SEG_DATA);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B);
break;
case PRAGMA_INLINE_STDFUNCS:
FlagPragma (&B, &InlineStdFuncs);
/* TODO: PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs);
break;
case PRAGMA_LOCAL_STRINGS:
FlagPragma (&B, &LocalStrings);
/* TODO: PES_STMT or even PES_EXPR */
FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings);
break;
case PRAGMA_MESSAGE:
StringPragma (&B, MakeMessage);
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, NoteMessagePragma);
break;
case PRAGMA_OPTIMIZE:
FlagPragma (&B, &Optimize);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &Optimize);
break;
case PRAGMA_REGVARADDR:
FlagPragma (&B, &AllowRegVarAddr);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr);
break;
case PRAGMA_REGVARS:
Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
/* FALLTHROUGH */
case PRAGMA_REGISTER_VARS:
FlagPragma (&B, &EnableRegVars);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars);
break;
case PRAGMA_RODATASEG:
Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
/* FALLTHROUGH */
case PRAGMA_RODATA_NAME:
SegNamePragma (&B, SEG_RODATA);
/* TODO: PES_STMT or even PES_EXPR maybe? */
SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B);
break;
case PRAGMA_SIGNEDCHARS:
Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
/* FALLTHROUGH */
case PRAGMA_SIGNED_CHARS:
FlagPragma (&B, &SignedChars);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &SignedChars);
break;
case PRAGMA_STATICLOCALS:
Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
/* FALLTHROUGH */
case PRAGMA_STATIC_LOCALS:
FlagPragma (&B, &StaticLocals);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals);
break;
case PRAGMA_WRAPPED_CALL:
WrappedCallPragma(&B);
/* PES_IMM is the only sensible option */
WrappedCallPragma (PES_IMM, &B);
break;
case PRAGMA_WARN:
WarnPragma (&B);
/* PES_IMM is the only sensible option */
WarnPragma (PES_IMM, &B);
break;
case PRAGMA_WRITABLE_STRINGS:
FlagPragma (&B, &WritableStrings);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings);
break;
case PRAGMA_ZPSYM:
StringPragma (&B, MakeZPSym);
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, MakeZPSym);
break;
default:
@ -975,20 +1082,31 @@ ExitPoint:
void DoPragma (void)
/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void ConsumePragma (void)
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
** operator.
*/
{
/* Skip the token itself */
/* Skip the _Pragma token */
NextToken ();
/* Prevent from translating string literals in _Pragma */
++InPragmaParser;
/* We expect an opening paren */
if (!ConsumeLParen ()) {
--InPragmaParser;
return;
}
/* String literal */
if (CurTok.Tok != TOK_SCONST) {
/* Print a diagnostic */
Error ("String literal expected");
@ -996,13 +1114,13 @@ void DoPragma (void)
** enclosing paren, or a semicolon.
*/
PragmaErrorSkip ();
} else {
/* Parse the _Pragma statement */
ParsePragma ();
/* Parse the pragma */
ParsePragmaString ();
}
--InPragmaParser;
/* Closing paren needed */
ConsumeRParen ();
}

View File

@ -44,8 +44,10 @@
void DoPragma (void);
/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
void ConsumePragma (void);
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
** operator.
*/

View File

@ -56,6 +56,7 @@
#include "ident.h"
#include "input.h"
#include "litpool.h"
#include "pragma.h"
#include "preproc.h"
#include "scanner.h"
#include "standard.h"
@ -75,9 +76,12 @@
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
static Token SavedTok; /* Saved token */
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
int NoCharMap; /* Disable literal translation */
unsigned InPragmaParser; /* Depth of pragma parser calling */
@ -329,7 +333,7 @@ static void SetTok (int tok)
static int ParseChar (void)
/* Parse a character. Converts escape chars into character codes. */
/* Parse a character token. Converts escape chars into character codes. */
{
int C;
int HadError;
@ -431,7 +435,7 @@ static int ParseChar (void)
static void CharConst (void)
/* Parse a character constant. */
/* Parse a character constant token */
{
int C;
@ -459,7 +463,7 @@ static void CharConst (void)
}
/* Translate into target charset */
NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
NextTok.IVal = SignExtendChar (C);
/* Character constants have type int */
NextTok.Type = type_int;
@ -468,7 +472,7 @@ static void CharConst (void)
static void StringConst (void)
/* Parse a quoted string */
/* Parse a quoted string token */
{
/* String buffer */
StrBuf S = AUTO_STRBUF_INITIALIZER;
@ -476,43 +480,34 @@ static void StringConst (void)
/* Assume next token is a string constant */
NextTok.Tok = TOK_SCONST;
/* Concatenate strings. If at least one of the concenated strings is a wide
** character literal, the whole string is a wide char literal, otherwise
** it's a normal string literal.
*/
while (1) {
/* Check if this is a normal or a wide char string */
if (CurC == 'L' && NextC == '\"') {
/* Wide character literal */
NextTok.Tok = TOK_WCSCONST;
NextChar ();
NextChar ();
} else if (CurC == '\"') {
/* Skip the quote char */
NextChar ();
} else {
/* No string */
goto ExitPoint;
}
/* Check if this is a normal or a wide char string */
if (CurC == 'L' && NextC == '\"') {
/* Wide character literal */
NextTok.Tok = TOK_WCSCONST;
NextChar ();
NextChar ();
} else if (CurC == '\"') {
/* Skip the quote char */
NextChar ();
} else {
/* No string */
/* Read until end of string */
while (CurC != '\"') {
if (CurC == '\0') {
Error ("Unexpected newline");
break;
}
/* Read until end of string */
while (CurC != '\"') {
if (CurC == '\0') {
Error ("Unexpected newline");
break;
}
SB_AppendChar (&S, ParseChar ());
}
/* Skip closing quote char if there was one */
NextChar ();
/* Skip white space, read new input */
SkipWhite ();
SB_AppendChar (&S, ParseChar ());
}
/* Skip closing quote char if there was one */
NextChar ();
ExitPoint:
/* Terminate the string */
SB_AppendChar (&S, '\0');
@ -526,7 +521,7 @@ static void StringConst (void)
static void NumericConst (void)
/* Parse a numeric constant */
/* Parse a numeric constant token */
{
unsigned Base; /* Temporary number base according to prefix */
unsigned Index;
@ -810,39 +805,49 @@ static void NumericConst (void)
void NextToken (void)
static void GetNextInputToken (void)
/* Get next token from input stream */
{
ident token;
/* We have to skip white space here before shifting tokens, since the
** tokens and the current line info is invalid at startup and will get
** initialized by reading the first time from the file. Remember if
** we were at end of input and handle that later.
*/
int GotEOF = (SkipWhite() == 0);
if (!NoCharMap && !InPragmaParser) {
/* Translate string and character literals into target charset */
if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) {
TranslateLiteral (NextTok.SVal);
} else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) {
NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal));
}
}
/* Current token is the lookahead token */
if (CurTok.LI) {
ReleaseLineInfo (CurTok.LI);
}
/* Get the current token */
CurTok = NextTok;
/* When reading the first time from the file, the line info in NextTok,
** which was copied to CurTok is invalid. Since the information from
** the token is used for error messages, we must make it valid.
*/
if (CurTok.LI == 0) {
CurTok.LI = UseLineInfo (GetCurLineInfo ());
}
if (SavedTok.Tok == TOK_INVALID) {
/* We have to skip white space here before shifting tokens, since the
** tokens and the current line info is invalid at startup and will get
** initialized by reading the first time from the file. Remember if we
** were at end of input and handle that later.
*/
int GotEOF = (SkipWhite () == 0);
/* Remember the starting position of the next token */
NextTok.LI = UseLineInfo (GetCurLineInfo ());
/* Remember the starting position of the next token */
NextTok.LI = UseLineInfo (GetCurLineInfo ());
/* Now handle end of input. */
if (GotEOF) {
/* End of file reached */
NextTok.Tok = TOK_CEOF;
/* Now handle end of input */
if (GotEOF) {
/* End of file reached */
NextTok.Tok = TOK_CEOF;
return;
}
} else {
/* Just use the saved token */
NextTok = SavedTok;
SavedTok.Tok = TOK_INVALID;
return;
}
@ -869,7 +874,8 @@ void NextToken (void)
if (!PPParserRunning) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
NextTok.Tok = FindKey (token);
if (NextTok.Tok != TOK_IDENT) {
/* Reserved word found */
return;
}
@ -1127,7 +1133,90 @@ void NextToken (void)
UnknownChar (CurC);
}
}
void NextToken (void)
/* Get next non-pragma token from input stream consuming any pragmas
** encountered. Adjacent string literal tokens will be concatenated.
*/
{
/* Used for string literal concatenation */
Token PrevTok;
/* When reading the first time from the file, the line info in NextTok,
** which will be copied to CurTok is invalid. Since the information from
** the token is used for error messages, we must make it valid.
*/
if (NextTok.LI == 0) {
NextTok.LI = UseLineInfo (GetCurLineInfo ());
}
PrevTok.Tok = TOK_INVALID;
while (1) {
/* Read the next token from the file */
GetNextInputToken ();
/* Consume all pragmas at hand, including those nested in a _Pragma() */
if (CurTok.Tok == TOK_PRAGMA) {
/* Repeated and/or nested _Pragma()'s will be handled recursively */
ConsumePragma ();
}
/* Check for string concatenation */
if (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST) {
if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) {
/* Warn on non-ISO behavior */
if (InPragmaParser) {
Warning ("Concatenated string literals in _Pragma operation");
}
/* Concatenate strings */
ConcatLiteral (PrevTok.SVal, CurTok.SVal);
/* If at least one of the concatenated strings is a wide
** character literal, the whole string is a wide char
** literal, otherwise it is a normal string literal.
*/
if (CurTok.Tok == TOK_WCSCONST) {
PrevTok.Tok = TOK_WCSCONST;
PrevTok.Type = CurTok.Type;
}
}
if (NextTok.Tok == TOK_SCONST ||
NextTok.Tok == TOK_WCSCONST ||
NextTok.Tok == TOK_PRAGMA) {
/* Remember current string literal token */
if (PrevTok.Tok == TOK_INVALID) {
PrevTok = CurTok;
PrevTok.LI = UseLineInfo (PrevTok.LI);
}
/* Keep looping */
continue;
}
}
break;
}
/* Use the concatenated string literal token if there is one */
if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) {
if (CurTok.Tok != TOK_SCONST && CurTok.Tok != TOK_WCSCONST) {
/* Push back the incoming tokens */
SavedTok = NextTok;
NextTok = CurTok;
} else {
/* The last string literal token can be just replaced */
if (CurTok.LI) {
ReleaseLineInfo (CurTok.LI);
}
}
/* Replace the current token with the concatenated string literal */
CurTok = PrevTok;
}
}

View File

@ -219,9 +219,11 @@ struct Token {
const Type* Type; /* Type if integer or float constant */
};
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
extern int NoCharMap; /* Disable literal translation */
extern unsigned InPragmaParser; /* Depth of pragma parser calling */
@ -299,7 +301,9 @@ void CopyPPNumber (StrBuf* Target);
/* Copy a pp-number from the input to Target */
void NextToken (void);
/* Get next token from input stream */
/* Get next non-pragma token from input stream consuming any pragmas
** encountered. Adjacent string literal tokens will be concatenated.
*/
void SkipTokens (const token_t* TokenList, unsigned TokenCount);
/* Skip tokens until we reach TOK_CEOF or a token in the given token list.

View File

@ -72,9 +72,15 @@ void ParseStaticAssert ()
** support the C2X syntax with only an expression.
*/
if (CurTok.Tok == TOK_COMMA) {
/* Skip the comma. */
/* Prevent from translating the message string literal */
NoCharMap = 1;
/* Skip the comma and get the next token */
NextToken ();
/* Reenable string literal translation */
NoCharMap = 0;
/* String literal */
if (CurTok.Tok != TOK_SCONST) {
Error ("String literal expected for static_assert message");

View File

@ -735,10 +735,6 @@ int AnyStatement (int* PendingToken)
GotBreak = 1;
break;
case TOK_PRAGMA:
DoPragma ();
break;
case TOK_SEMI:
/* Empty statement. Ignore it */
CheckSemi (PendingToken);

161
test/val/booltrans.c Normal file
View File

@ -0,0 +1,161 @@
/* Optimization bugs with multiple inverse Z branches following one boolean transformer */
#include <stdint.h>
#include <stdio.h>
unsigned failures;
int a;
/* To reveal the bug, the second Z branch must jump over the destination of the first Z branch */
int test_booltrans(int8_t x)
{
a = x;
__asm__("lda #$00");
__asm__("cmp %v", a);
__asm__("jsr booleq");
__asm__("jeq %g", L1);
__asm__("jne %g", L0);
L1:
return 1;
L0:
return 0;
}
int test_bnega2(int8_t x)
{
a = x;
__asm__("lda %v", a);
__asm__("jsr bnega");
__asm__("jeq %g", L1);
__asm__("jne %g", L0);
L1:
return 1;
L0:
return 0;
}
int test_bnegax2(int16_t x)
{
int a = x;
__asm__("ldy #%o+1", a);
__asm__("jsr ldaxysp");
__asm__("jsr bnegax");
__asm__("jeq %g", L1);
__asm__("jne %g", L0);
L1:
return 1;
L0:
return 0;
}
void __fastcall__ f(void) {}
int test_bnegax3(int16_t x)
{
a = x;
__asm__("lda %v", a);
__asm__("ldx %v+1", a);
__asm__("jsr %v", f);
__asm__("jsr bnegax");
__asm__("jeq %g", L1);
__asm__("jne %g", L0);
L1:
return 1;
L0:
return 0;
}
int test_bnegax4(int16_t x)
{
a = x;
__asm__("lda %v", a);
__asm__("ldx %v+1", a);
__asm__("jsr bnegax");
__asm__("jeq %g", L1);
__asm__("jne %g", L0);
L1:
return 1;
L0:
return 0;
}
int main(void)
{
a = test_booltrans(0);
if (a != 0)
{
++failures;
printf("test_booltrans(0): %d, expected: 0\n", a);
}
a = test_booltrans(1);
if (a != 1)
{
++failures;
printf("test_booltrans(1): %d, expected: 1\n", a);
}
a = test_bnega2(0);
if (a != 0)
{
++failures;
printf("test_bnega2(0): %d, expected: 0\n", a);
}
a = test_bnega2(1);
if (a != 1)
{
++failures;
printf("test_bnega2(1): %d, expected: 1\n", a);
}
a = test_bnegax2(0);
if (a != 0)
{
++failures;
printf("test_bnegax2(0): %d, expected: 0\n", a);
}
a = test_bnegax2(1);
if (a != 1)
{
++failures;
printf("test_bnegax2(1): %d, expected: 1\n", a);
}
a = test_bnegax3(0);
if (a != 0)
{
++failures;
printf("test_bnegax3(0): %d, expected: 0\n", a);
}
a = test_bnegax3(1);
if (a != 1)
{
++failures;
printf("test_bnegax3(1): %d, expected: 1\n", a);
}
a = test_bnegax4(0);
if (a != 0)
{
++failures;
printf("test_bnegax4(0): %d, expected: 0\n", a);
}
a = test_bnegax4(1);
if (a != 1)
{
++failures;
printf("test_bnegax4(1): %d, expected: 1\n", a);
}
if (failures > 0)
{
printf("failures: %u\n", failures);
}
return failures;
}

77
test/val/bug2151.c Normal file
View File

@ -0,0 +1,77 @@
/* Bug #2151 - #pragma causes errors when used within functions */
#include <stdio.h>
#include <string.h>
#pragma charmap(0x61, 0x61)
_Static_assert('A'==
#pragma charmap(0x61, 0x41)
'a'
#pragma charmap(0x61, 0x42)
,
#pragma charmap(0x61, 0x61)
"charmap failed");
char str[] =
"a"
#pragma charmap(0x61, 0x42)
"a"
#pragma charmap(0x61, 0x43)
"a"
#pragma charmap(0x61, 0x61)
;
unsigned failures;
#pragma bss-name("BSS1")
int
#pragma code-name("CODE_WUT")
main _Pragma
#pragma charmap(0x61, 0x32)
(
"message(\"_Pragma string"
/* Concatenated string literals in _Pragma is a cc65 extension */
" unaffected by charmap\")"
)
#pragma charmap(0x61, 0x61)
(
void
_Pragma _Pragma (
#pragma message("nested message 1")
"message(\"nested message 2\")"
)
(
"message(\"_Pragma in function parentheses\")")
#pragma code-name("CODE")
)
#pragma bss-name("BSS")
{
extern int y;
#pragma bss-name("BSS2")
static
#pragma zpsym ("y")
int x; // TODO: currently in "BSS", but supposed to be in "BSS2"?
x = 0;
if (memcmp(str, "aBC", 3))
{
++failures;
printf("%3s\n", str);
}
if (x + y != 0)
{
++failures;
printf("%d\n", x + y);
}
if (failures != 0)
{
printf("faiures: %d\n", failures);
}
return failures;
#pragma bss-name("BSS")
}
int y;

39
test/val/bug2186.c Normal file
View File

@ -0,0 +1,39 @@
/* Bug #2186 - Wrong array indexing when index comes from bit-field */
#include <stdio.h>
unsigned failures;
typedef struct {
char flag : 1;
char index : 7;
} weird_type;
const char array[] = { '6', '5', '0', '2' };
weird_type data;
int main(void) {
data.flag = 1;
data.index = 0;
if (array[data.index] != array[0])
{
++failures;
printf("Got '%c', expected '%c'\n", array[data.index], array[0]);
}
data.index = 1;
if (array[data.index] != array[1])
{
++failures;
printf("Got '%c', expected '%c'\n", array[data.index], array[1]);
}
if (failures > 0)
{
printf("Failures: %u\n", failures);
}
return failures;
}