mirror of
https://github.com/cc65/cc65.git
synced 2024-06-09 06:29:38 +00:00
Merge branch 'master' into fptest
This commit is contained in:
commit
1db3d87ac8
|
@ -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
|
||||
|
|
|
@ -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] (<string literal>[, optional parameters]) ;
|
||||
asm [optional volatile] (<string literal>[, optional parameters])
|
||||
</verb></tscreen>
|
||||
or
|
||||
<tscreen><verb>
|
||||
__asm__ [optional volatile] (<string literal>[, optional parameters]) ;
|
||||
__asm__ [optional volatile] (<string literal>[, 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 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 (<index>, <code>)</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] (<string literal>[, optional parameters]) ;
|
||||
asm [optional volatile] (<string literal>[, optional parameters])
|
||||
</verb></tscreen>
|
||||
or
|
||||
<tscreen><verb>
|
||||
__asm__ [optional volatile] (<string literal>[, optional parameters]) ;
|
||||
__asm__ [optional volatile] (<string literal>[, 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 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
942
src/cc65/coptbool.c
Normal 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;
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
236
src/cc65/coptunary.c
Normal 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
96
src/cc65/coptunary.h
Normal 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
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
161
test/val/booltrans.c
Normal 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
77
test/val/bug2151.c
Normal 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
39
test/val/bug2186.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user