Merge pull request #151 from greg-king5/fastcall

Make __fastcall__ be the default calling convention.
This commit is contained in:
Oliver Schmidt
2015-05-26 22:39:33 +02:00
24 changed files with 257 additions and 147 deletions

View File

@@ -3,7 +3,7 @@
<article> <article>
<title>cc65 Users Guide <title>cc65 Users Guide
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> <author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">
<date>2000-09-03, 2001-10-02, 2005-08-01 <date>2015-05-26
<abstract> <abstract>
cc65 is a C compiler for 6502 targets. It supports several 6502 based home cc65 is a C compiler for 6502 targets. It supports several 6502 based home
@@ -74,6 +74,7 @@ Short options:
Long options: Long options:
--add-source Include source as comment --add-source Include source as comment
--all-cdecl Make functions default to __cdecl__
--bss-name seg Set the name of the BSS segment --bss-name seg Set the name of the BSS segment
--check-stack Generate stack overflow checks --check-stack Generate stack overflow checks
--code-name seg Set the name of the CODE segment --code-name seg Set the name of the CODE segment
@@ -114,6 +115,14 @@ Here is a description of all the command line options:
<descrip> <descrip>
<tag><tt>--all-cdecl</tt></tag>
Tells the compiler that functions which aren't declared explicitly with
either the <tt/__cdecl__/ or <tt/__fastcall__/ calling conventions should
have the cdecl convention. (Normally, functions that aren't variadic are
fast-called.)
<label id="option-bss-name"> <label id="option-bss-name">
<tag><tt>--bss-name seg</tt></tag> <tag><tt>--bss-name seg</tt></tag>
@@ -550,9 +559,10 @@ and the one defined by the ISO standard:
be passed as parameters by value. However, struct assignment *is* be passed as parameters by value. However, struct assignment *is*
possible. possible.
<p> <p>
<item> Part of the C library is available only with fastcall calling <item> Most of the C library is available with only the fastcall calling
conventions (see below). It means that you must not mix pointers to convention (<ref id="extension-fastcall" name="see below">). It means
those functions with pointers to user-written, not-fastcall functions. that you must not mix pointers to those functions with pointers to
user-written, cdecl functions (the calling conventions are incompatible).
<p> <p>
<item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad <item> The <tt/volatile/ keyword doesn't have an effect. This is not as bad
as it sounds, since the 6502 has so few registers that it isn't as it sounds, since the 6502 has so few registers that it isn't
@@ -590,30 +600,58 @@ This cc65 version has some extensions to the ISO C standard.
<ref id="inline-asm" name="see there">. <ref id="inline-asm" name="see there">.
<p> <p>
<item> There is a special calling convention named "fastcall". <label id="extension-fastcall">
The syntax for a function declaration using fastcall is <item> The normal calling convention -- for non-variadic functions -- is
named "fastcall". The syntax for a function declaration that
<em/explicitly/ uses fastcall is
<tscreen><verb> <tscreen><verb>
&lt;return type&gt; fastcall &lt;function name&gt; (&lt;parameter list&gt;) &lt;return type&gt; fastcall &lt;function name&gt; (&lt;parameter list&gt;)
</verb></tscreen> </verb></tscreen>
or or
<tscreen><verb> <tscreen><verb>
&lt;return type&gt; __fastcall__ &lt;function name&gt; (&lt;parameter list&gt;) &lt;return type&gt; __fastcall__ &lt;function name&gt; (&lt;parameter list&gt;)
</verb></tscreen> </verb></tscreen>
An example would be An example is
<tscreen><verb> <tscreen><verb>
void __fastcall__ f (unsigned char c) void __fastcall__ f (unsigned char c)
</verb></tscreen> </verb></tscreen>
The first form of the fastcall keyword is in the user namespace and can The first form of the fastcall keyword is in the user namespace and can
therefore be disabled with the <tt><ref id="option--standard" therefore be disabled with the <tt><ref id="option--standard"
name="--standard"></tt> command line option. name="--standard"></tt> command line option.
For functions declared as <tt/fastcall/, the rightmost parameter is not For functions that are <tt/fastcall/, the rightmost parameter is not
pushed on the stack but left in the primary register when the function pushed on the stack but left in the primary register when the function
is called. This will reduce the cost when calling assembler functions is called. That significantly reduces the cost of calling those functions.
significantly, especially when the function itself is rather small. <newline><newline>
<p> <p>
<item> There is another calling convention named "cdecl". Variadic functions
(their prototypes have an ellipsis &lsqb;<tt/.../&rsqb;) always use that
convention. The syntax for a function declaration using cdecl is
<tscreen><verb>
&lt;return type&gt; cdecl &lt;function name&gt; (&lt;parameter list&gt;)
</verb></tscreen>
or
<tscreen><verb>
&lt;return type&gt; __cdecl__ &lt;function name&gt; (&lt;parameter list&gt;)
</verb></tscreen>
An example is
<tscreen><verb>
int* __cdecl__ f (unsigned char c)
</verb></tscreen>
The first form of the cdecl keyword is in the user namespace;
and therefore, can be disabled with the <tt/<ref id="option--standard"
name="--standard">/ command-line option.
For functions that are <tt/cdecl/, the rightmost parameter is pushed
onto the stack before the function is called. That increases the cost
of calling those functions, especially when they are called from many
places.<newline><newline>
<p>
<item> There are two pseudo variables named <tt/__AX__/ and <tt/__EAX__/. <item> There are two pseudo variables named <tt/__AX__/ and <tt/__EAX__/.
Both refer to the primary register that is used by the compiler to Both refer to the primary register that is used by the compiler to
evaluate expressions or return function results. <tt/__AX__/ is of evaluate expressions or return function results. <tt/__AX__/ is of

View File

@@ -3,7 +3,7 @@
<article> <article>
<title>Defining a Custom cc65 Target <title>Defining a Custom cc65 Target
<author>Bruce Reidenbach <author>Bruce Reidenbach
<date>2010-02-22 <date>2015-03-13
<abstract> <abstract>
This section provides step-by-step instructions on how to use the cc65 This section provides step-by-step instructions on how to use the cc65
@@ -525,15 +525,16 @@ The first step in creating the assembly language code for the driver is
to determine how to pass the C arguments to the assembly language to determine how to pass the C arguments to the assembly language
routine. The cc65 toolset allows the user to specify whether the data routine. The cc65 toolset allows the user to specify whether the data
is passed to a subroutine via the stack or by the processor registers by is passed to a subroutine via the stack or by the processor registers by
using the <tt>__fastcall__</tt> function declaration (note that there using the <tt/__fastcall__/ and <tt/__cdecl__/ function qualifiers (note that
are two underscore characters in front of and two behind the there are two underscore characters in front of and two behind each
<tt>fastcall</tt> declaration). When <tt>__fastcall__</tt> is qualifier). <tt/__fastcall__/ is the default. When <tt/__cdecl__/ <em/isn't/
specified, the rightmost argument in the function call is passed to the specified, and the function isn't variadic (i.e., its prototype doesn't have
an ellipsis), the rightmost argument in the function call is passed to the
subroutine using the 6502 registers instead of the stack. Note that if subroutine using the 6502 registers instead of the stack. Note that if
there is only one argument in the function call, the execution overhead there is only one argument in the function call, the execution overhead
required by the stack interface routines is completely avoided. required by the stack interface routines is completely avoided.
Without <tt>__fastcall__</tt>, the argument is loaded in the A and X With <tt/__cdecl__</tt>, the last argument is loaded into the A and X
registers and then pushed onto the stack via a call to <tt>pushax</tt>. registers and then pushed onto the stack via a call to <tt>pushax</tt>.
The first thing the subroutine does is retrieve the argument from the The first thing the subroutine does is retrieve the argument from the
stack via a call to <tt>ldax0sp</tt>, which copies the values into the A stack via a call to <tt>ldax0sp</tt>, which copies the values into the A
@@ -561,7 +562,7 @@ _foo: jsr ldax0sp ; Retrieve A and X from the stack
jmp incsp2 ; Pop A and X from the stack (includes return) jmp incsp2 ; Pop A and X from the stack (includes return)
</verb></tscreen> </verb></tscreen>
If <tt>__fastcall__</tt> is specified, the argument is loaded into the A If <tt/__cdecl__/ isn't specified, then the argument is loaded into the A
and X registers as before, but the subroutine is then called and X registers as before, but the subroutine is then called
immediately. The subroutine does not need to retrieve the argument immediately. The subroutine does not need to retrieve the argument
since the value is already available in the A and X registers. since the value is already available in the A and X registers.

View File

@@ -2,14 +2,14 @@
/* */ /* */
/* ace.h */ /* ace.h */
/* */ /* */
/* ACE system specific definitions */ /* ACE system-specific definitions */
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2001 Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Wacholderweg 14 */ /* Roemerstrasse 52 */
/* D-70597 Stuttgart */ /* D-70794 Filderstadt */
/* EMail: uz@musoftware.de */ /* EMail: uz@cc65.org */
/* */ /* */
/* */ /* */
/* This software is provided 'as-is', without any expressed or implied */ /* This software is provided 'as-is', without any expressed or implied */
@@ -61,9 +61,9 @@ struct aceDirentBuf {
char ad_name [17]; /* Name itself, ASCIIZ */ char ad_name [17]; /* Name itself, ASCIIZ */
}; };
int aceDirOpen (char* dir); int __cdecl__ aceDirOpen (char* dir);
int aceDirClose (int handle); int __cdecl__ aceDirClose (int handle);
int aceDirRead (int handle, struct aceDirentBuf* buf); int __cdecl__ aceDirRead (int handle, struct aceDirentBuf* buf);
/* Type of an ACE key. Key in low byte, shift mask in high byte */ /* Type of an ACE key. Key in low byte, shift mask in high byte */
typedef unsigned int aceKey; typedef unsigned int aceKey;
@@ -92,23 +92,23 @@ typedef unsigned int aceKey;
#define aceOP_RPTRATE 11 /* Key repeat rate */ #define aceOP_RPTRATE 11 /* Key repeat rate */
/* Console functions */ /* Console functions */
void aceConWrite (char* buf, size_t count); void __cdecl__ aceConWrite (char* buf, size_t count);
void aceConPutLit (int c); void __cdecl__ aceConPutLit (int c);
void aceConPos (unsigned x, unsigned y); void __cdecl__ aceConPos (unsigned x, unsigned y);
void aceConGetPos (unsigned* x, unsigned* y); void __cdecl__ aceConGetPos (unsigned* x, unsigned* y);
unsigned aceConGetX (void); unsigned aceConGetX (void);
unsigned aceConGetY (void); unsigned aceConGetY (void);
char* aceConInput (char* buf, unsigned initial); char __cdecl__* aceConInput (char* buf, unsigned initial);
int aceConStopKey (void); int aceConStopKey (void);
aceKey aceConGetKey (void); aceKey aceConGetKey (void);
int aceConKeyAvail (aceKey* key); int __cdecl__ aceConKeyAvail (aceKey* key);
void aceConKeyMat (char* matrix); void __cdecl__ aceConKeyMat (char* matrix);
void aceConSetOpt (unsigned char opt, unsigned char val); void __cdecl__ aceConSetOpt (unsigned char opt, unsigned char val);
int aceConGetOpt (unsigned char opt); int __cdecl__ aceConGetOpt (unsigned char opt);
/* Misc stuff */ /* Misc stuff */
int aceMiscIoPeek (unsigned addr); int __cdecl__ aceMiscIoPeek (unsigned addr);
void aceMiscIoPoke (unsigned addr, unsigned char val); void __cdecl__ aceMiscIoPoke (unsigned addr, unsigned char val);

View File

@@ -6,10 +6,10 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2000 Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Wacholderweg 14 */ /* Roemerstrasse 52 */
/* D-70597 Stuttgart */ /* D-70794 Filderstadt */
/* EMail: uz@musoftware.de */ /* EMail: uz@cc65.org */
/* */ /* */
/* */ /* */
/* This software is provided 'as-is', without any expressed or implied */ /* This software is provided 'as-is', without any expressed or implied */
@@ -42,7 +42,7 @@
#ifdef NDEBUG #ifdef NDEBUG
# define assert(expr) # define assert(expr)
#else #else
extern void _afailed (const char*, unsigned); extern void __fastcall__ _afailed (const char*, unsigned);
# define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__))
#endif #endif

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2012, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -222,7 +222,7 @@ void cbm_k_unlsn (void);
unsigned int cbm_load (const char* name, unsigned char device, void* data); unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void* data);
/* Loads file "name", from given device, to given address -- or, to the load /* Loads file "name", from given device, to given address -- or, to the load
** address of the file if "data" is the null pointer (like load"name",8,1 ** address of the file if "data" is the null pointer (like load"name",8,1
** in BASIC). ** in BASIC).

View File

@@ -6,10 +6,10 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2000 Ullrich von Bassewitz */ /* (C) 1998-2000, Ullrich von Bassewitz */
/* Wacholderweg 14 */ /* Roemerstrasse 52 */
/* D-70597 Stuttgart */ /* D-70794 Filderstadt */
/* EMail: uz@musoftware.de */ /* EMail: uz@cc65.org */
/* */ /* */
/* */ /* */
/* This software is provided 'as-is', without any expressed or implied */ /* This software is provided 'as-is', without any expressed or implied */
@@ -88,7 +88,7 @@ unsigned __fastcall__ DbgDisAsmLen (unsigned Addr);
int __fastcall__ DbgIsRAM (unsigned Addr); int __fastcall__ DbgIsRAM (unsigned Addr);
/* Return true if we can read and write the given address */ /* Return true if we can read and write the given address */
char* DbgMemDump (unsigned Addr, char* Buf, unsigned char Len); char* __cdecl__ DbgMemDump (unsigned Addr, char* Buf, unsigned char Len);
/* Create a line of a memory dump in the given buffer. The buffer contains /* Create a line of a memory dump in the given buffer. The buffer contains
** the starting address (4 digits hex), then Len bytes in this format: ** the starting address (4 digits hex), then Len bytes in this format:
** "AAAA__XX_YY_ZZ_...". The passed char buffer must hold Len*3+5 bytes ** "AAAA__XX_YY_ZZ_...". The passed char buffer must hold Len*3+5 bytes

View File

@@ -2,7 +2,7 @@
/* */ /* */
/* lynx.h */ /* lynx.h */
/* */ /* */
/* Lynx system specific definitions */ /* Lynx system-specific definitions */
/* */ /* */
/* */ /* */
/* */ /* */
@@ -109,25 +109,25 @@ extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */
/* Sound support */ /* Sound support */
/*****************************************************************************/ /*****************************************************************************/
void lynx_snd_init (); void lynx_snd_init (void);
/* Initialize the sound driver */ /* Initialize the sound driver */
void lynx_snd_pause (); void lynx_snd_pause (void);
/* Pause sound */ /* Pause sound */
void lynx_snd_continue (); void lynx_snd_continue (void);
/* Continue sound after pause */ /* Continue sound after pause */
void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music); void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music);
/* Play tune on channel */ /* Play tune on channel */
void lynx_snd_stop (); void lynx_snd_stop (void);
/* Stop sound on all channels */ /* Stop sound on all channels */
void __fastcall__ lynx_snd_stop_channel (unsigned char channel); void __fastcall__ lynx_snd_stop_channel (unsigned char channel);
/* Stop sound on all channels */ /* Stop sound on all channels */
unsigned char lynx_snd_active(); unsigned char lynx_snd_active(void);
/* Show which channels are active */ /* Show which channels are active */
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 2000-2001 Piotr Fusik <fox@scene.pl> */ /* (C) 2000-2015 Piotr Fusik <fox@scene.pl> */
/* */ /* */
/* This file is based on the zlib.h from 'zlib' general purpose compression */ /* This file is based on the zlib.h from 'zlib' general purpose compression */
/* library, version 1.1.3, (C) 1995-1998 Jean-loup Gailly and Mark Adler. */ /* library, version 1.1.3, (C) 1995-1998 Jean-loup Gailly and Mark Adler. */
@@ -83,8 +83,8 @@ unsigned __fastcall__ inflatemem (char* dest, const char* source);
*/ */
int uncompress (char* dest, unsigned* destLen, int __fastcall__ uncompress (char* dest, unsigned* destLen,
const char* source, unsigned sourceLen); const char* source, unsigned sourceLen);
/* /*
Original zlib description: Original zlib description:

View File

@@ -1,9 +1,9 @@
/* /*
** Marc 'BlackJack' Rintsch, 06.03.2001 ** Marc 'BlackJack' Rintsch, 06.03.2001
** **
** unsigned int cbm_load(const char* name, ** unsigned int __fastcall__ cbm_load(const char* name,
** unsigned char device, ** unsigned char device,
** const unsigned char* data); ** const unsigned char* data);
*/ */
#include <cbm.h> #include <cbm.h>
@@ -11,7 +11,7 @@
/* loads file "name" from given device to given address or to the load address /* loads file "name" from given device to given address or to the load address
** of the file if "data" is 0 ** of the file if "data" is 0
*/ */
unsigned int cbm_load(const char* name, unsigned char device, void* data) unsigned int __fastcall__ cbm_load(const char* name, unsigned char device, void* data)
{ {
/* LFN is set to 0; but, it's not needed for loading /* LFN is set to 0; but, it's not needed for loading
** (BASIC V2 sets it to the value of the SA for LOAD). ** (BASIC V2 sets it to the value of the SA for LOAD).

View File

@@ -1,7 +1,8 @@
/* /*
** _afailed.c ** _afailed.c
** **
** Ullrich von Bassewitz, 06.06.1998 ** 1998-06-06, Ullrich von Bassewitz
** 2015-03-13, Greg King
*/ */
@@ -11,7 +12,7 @@
void _afailed (char* file, unsigned line) void __fastcall__ _afailed (char* file, unsigned line)
{ {
fprintf (stderr, "ASSERTION FAILED IN %s(%u)\n", file, line); fprintf (stderr, "ASSERTION FAILED IN %s(%u)\n", file, line);
exit (2); exit (2);

View File

@@ -1,7 +1,7 @@
; ;
; Ullrich von Bassewitz, 11.08.1998 ; Ullrich von Bassewitz, 11.08.1998
; ;
; char* DbgMemDump (unsigend Addr, char* Buf, unsigned char Length); ; char* __cdecl__ DbgMemDump (unsigend Addr, char* Buf, unsigned char Length);
; ;
.export _DbgMemDump .export _DbgMemDump

View File

@@ -6,8 +6,8 @@
#include <zlib.h> #include <zlib.h>
int uncompress (char* dest, unsigned* destLen, int __fastcall__ uncompress (char* dest, unsigned* destLen,
const char* source, unsigned sourceLen) const char* source, unsigned sourceLen)
{ {
unsigned len; unsigned len;
unsigned char* ptr; unsigned char* ptr;

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 2001-2012, Ullrich von Bassewitz */ /* (C) 2001-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -46,6 +46,7 @@
#include "codeseg.h" #include "codeseg.h"
#include "datatype.h" #include "datatype.h"
#include "error.h" #include "error.h"
#include "global.h"
#include "reginfo.h" #include "reginfo.h"
#include "symtab.h" #include "symtab.h"
#include "codeinfo.h" #include "codeinfo.h"
@@ -386,33 +387,35 @@ void GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
** Search for it in the list of builtin functions. ** Search for it in the list of builtin functions.
*/ */
if (Name[0] == '_') { if (Name[0] == '_') {
/* Search in the symbol table, skip the leading underscore */ /* Search in the symbol table, skip the leading underscore */
SymEntry* E = FindGlobalSym (Name+1); SymEntry* E = FindGlobalSym (Name+1);
/* Did we find it in the top level table? */ /* Did we find it in the top-level table? */
if (E && IsTypeFunc (E->Type)) { if (E && IsTypeFunc (E->Type)) {
FuncDesc* D = E->V.F.Func; FuncDesc* D = E->V.F.Func;
/* A function may use the A or A/X registers if it is a fastcall /* A variadic function will use the Y register (the parameter list
** function. If it is not a fastcall function but a variadic one, ** size is passed there). A fastcall function will use the A or A/X
** it will use the Y register (the parameter size is passed here). ** registers. In all other cases, no registers are used. However,
** In all other cases, no registers are used. However, we assume ** we assume that any function will destroy all registers.
** that any function will destroy all registers.
*/ */
if (IsQualFastcall (E->Type) && D->ParamCount > 0) { if ((D->Flags & FD_VARIADIC) != 0) {
/* Will use registers depending on the last param */
unsigned LastParamSize = CheckedSizeOf (D->LastParam->Type);
if (LastParamSize == 1) {
*Use = REG_A;
} else if (LastParamSize == 2) {
*Use = REG_AX;
} else {
*Use = REG_EAX;
}
} else if ((D->Flags & FD_VARIADIC) != 0) {
*Use = REG_Y; *Use = REG_Y;
} else if (D->ParamCount > 0 &&
(AutoCDecl ?
IsQualFastcall (E->Type) :
!IsQualCDecl (E->Type))) {
/* Will use registers depending on the last param. */
switch (CheckedSizeOf (D->LastParam->Type)) {
case 1u:
*Use = REG_A;
break;
case 2u:
*Use = REG_AX;
break;
default:
*Use = REG_EAX;
}
} else { } else {
/* Will not use any registers */ /* Will not use any registers */
*Use = REG_NONE; *Use = REG_NONE;

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2012, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -293,15 +293,15 @@ void PrintType (FILE* F, const Type* T)
/* Recursive call */ /* Recursive call */
PrintType (F, T + 1); PrintType (F, T + 1);
if (T->A.L == UNSPECIFIED) { if (T->A.L == UNSPECIFIED) {
fprintf (F, "[]"); fprintf (F, " []");
} else { } else {
fprintf (F, "[%ld]", T->A.L); fprintf (F, " [%ld]", T->A.L);
} }
return; return;
case T_TYPE_PTR: case T_TYPE_PTR:
/* Recursive call */ /* Recursive call */
PrintType (F, T + 1); PrintType (F, T + 1);
fprintf (F, "*"); fprintf (F, " *");
return; return;
case T_TYPE_FUNC: case T_TYPE_FUNC:
fprintf (F, "function returning "); fprintf (F, "function returning ");

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2012, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -603,6 +603,16 @@ INLINE int IsQualCDecl (const Type* T)
# define IsQualCDecl(T) (((T)->C & T_QUAL_CDECL) != 0) # define IsQualCDecl(T) (((T)->C & T_QUAL_CDECL) != 0)
#endif #endif
#if defined(HAVE_INLINE)
INLINE int IsQualCConv (const Type* T)
/* Return true if the given type has a calling convention qualifier */
{
return (T->C & T_QUAL_CCONV) != 0;
}
#else
# define IsQualCConv(T) (((T)->C & T_QUAL_CCONV) != 0)
#endif
int IsVariadicFunc (const Type* T) attribute ((const)); int IsVariadicFunc (const Type* T) attribute ((const));
/* Return true if this is a function type or pointer to function type with /* Return true if this is a function type or pointer to function type with
** variable parameter list ** variable parameter list

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2013, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -85,7 +85,7 @@ struct StructInitData {
static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers);
/* Parse a type specificier */ /* Parse a type specifier */
static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers); static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers);
/* Parse initialization of variables. Return the number of data bytes. */ /* Parse initialization of variables. Return the number of data bytes. */
@@ -335,18 +335,30 @@ static void FixQualifiers (Type* DataType)
T = DataType; T = DataType;
while (T->C != T_END) { while (T->C != T_END) {
if (IsTypePtr (T)) { if (IsTypePtr (T)) {
/* Calling convention qualifier on the pointer? */
if (IsQualCConv (T)) {
/* Pull the convention off of the pointer */
Q = T[0].C & T_QUAL_CCONV;
T[0].C &= ~T_QUAL_CCONV;
/* Fastcall qualifier on the pointer? */ /* Pointer to a function which doesn't have an explicit convention? */
if (IsQualFastcall (T)) { if (IsTypeFunc (T + 1)) {
/* Pointer to function which is not fastcall? */ if (IsQualCConv (T + 1)) {
if (IsTypeFunc (T+1) && !IsQualFastcall (T+1)) { if ((T[1].C & T_QUAL_CCONV) == Q) {
/* Move the fastcall qualifier from the pointer to Warning ("Pointer duplicates function's calling convention");
** the function. } else {
*/ Error ("Function's and pointer's calling conventions are different");
T[0].C &= ~T_QUAL_FASTCALL; }
T[1].C |= T_QUAL_FASTCALL; } else {
if (Q == T_QUAL_FASTCALL && IsVariadicFunc (T + 1)) {
Error ("Variadic-function pointers cannot be __fastcall__");
} else {
/* Move the qualifier from the pointer to the function. */
T[1].C |= Q;
}
}
} else { } else {
Error ("Invalid `_fastcall__' qualifier for pointer"); Error ("Not pointer to a function; can't use a calling convention");
} }
} }
@@ -355,8 +367,8 @@ static void FixQualifiers (Type* DataType)
if (Q == T_QUAL_NONE) { if (Q == T_QUAL_NONE) {
/* No address size qualifiers specified */ /* No address size qualifiers specified */
if (IsTypeFunc (T+1)) { if (IsTypeFunc (T+1)) {
/* Pointer to function. Use the qualifier from the function /* Pointer to function. Use the qualifier from the function,
** or the default if the function don't has one. ** or the default if the function doesn't have one.
*/ */
Q = (T[1].C & T_QUAL_ADDRSIZE); Q = (T[1].C & T_QUAL_ADDRSIZE);
if (Q == T_QUAL_NONE) { if (Q == T_QUAL_NONE) {
@@ -368,7 +380,7 @@ static void FixQualifiers (Type* DataType)
T[0].C |= Q; T[0].C |= Q;
} else { } else {
/* We have address size qualifiers. If followed by a function, /* We have address size qualifiers. If followed by a function,
** apply these also to the function. ** apply them to the function also.
*/ */
if (IsTypeFunc (T+1)) { if (IsTypeFunc (T+1)) {
TypeCode FQ = (T[1].C & T_QUAL_ADDRSIZE); TypeCode FQ = (T[1].C & T_QUAL_ADDRSIZE);
@@ -489,7 +501,7 @@ static void ParseEnumDecl (void)
static int ParseFieldWidth (Declaration* Decl) static int ParseFieldWidth (Declaration* Decl)
/* Parse an optional field width. Returns -1 if no field width is speficied, /* Parse an optional field width. Returns -1 if no field width is specified,
** otherwise the width of the field. ** otherwise the width of the field.
*/ */
{ {
@@ -862,7 +874,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers)
/* Parse a type specificier */ /* Parse a type specifier */
{ {
ident Ident; ident Ident;
SymEntry* Entry; SymEntry* Entry;
@@ -1376,13 +1388,13 @@ static FuncDesc* ParseFuncDecl (void)
static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
/* Recursively process declarators. Build a type array in reverse order. */ /* Recursively process declarators. Build a type array in reverse order. */
{ {
/* Read optional function or pointer qualifiers. These modify the /* Read optional function or pointer qualifiers. They modify the
** identifier or token to the right. For convenience, we allow the fastcall ** identifier or token to the right. For convenience, we allow a calling
** qualifier also for pointers here. If it is a pointer-to-function, the ** convention also for pointers here. If it's a pointer-to-function, the
** qualifier will later be transfered to the function itself. If it's a ** qualifier later will be transfered to the function itself. If it's a
** pointer to something else, it will be flagged as an error. ** pointer to something else, it will be flagged as an error.
*/ */
TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_FASTCALL); TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_CCONV);
/* Pointer to something */ /* Pointer to something */
if (CurTok.Tok == TOK_STAR) { if (CurTok.Tok == TOK_STAR) {
@@ -1390,10 +1402,10 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
/* Skip the star */ /* Skip the star */
NextToken (); NextToken ();
/* Allow const, restrict and volatile qualifiers */ /* Allow const, restrict, and volatile qualifiers */
Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT); Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT);
/* Parse the type, the pointer points to */ /* Parse the type that the pointer points to */
Declarator (Spec, D, Mode); Declarator (Spec, D, Mode);
/* Add the type */ /* Add the type */
@@ -1443,7 +1455,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
/* We cannot specify fastcall for variadic functions */ /* We cannot specify fastcall for variadic functions */
if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) {
Error ("Variadic functions cannot be `__fastcall__'"); Error ("Variadic functions cannot be __fastcall__");
Qualifiers &= ~T_QUAL_FASTCALL; Qualifiers &= ~T_QUAL_FASTCALL;
} }

View File

@@ -1,6 +1,7 @@
/* expr.c /* expr.c
** **
** Ullrich von Bassewitz, 21.06.1998 ** 1998-06-21, Ullrich von Bassewitz
** 2015-04-19, Greg King
*/ */
@@ -470,9 +471,11 @@ static void FunctionCall (ExprDesc* Expr)
/* Handle function pointers transparently */ /* Handle function pointers transparently */
IsFuncPtr = IsTypeFuncPtr (Expr->Type); IsFuncPtr = IsTypeFuncPtr (Expr->Type);
if (IsFuncPtr) { if (IsFuncPtr) {
/* Check whether it's a fastcall function that has parameters */
/* Check wether it's a fastcall function that has parameters */ IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && Func->ParamCount > 0 &&
IsFastcall = IsQualFastcall (Expr->Type + 1) && (Func->ParamCount > 0); (AutoCDecl ?
IsQualFastcall (Expr->Type + 1) :
!IsQualCDecl (Expr->Type + 1));
/* Things may be difficult, depending on where the function pointer /* Things may be difficult, depending on where the function pointer
** resides. If the function pointer is an expression of some sort ** resides. If the function pointer is an expression of some sort
@@ -517,7 +520,10 @@ static void FunctionCall (ExprDesc* Expr)
} }
/* If we didn't inline the function, get fastcall info */ /* If we didn't inline the function, get fastcall info */
IsFastcall = IsQualFastcall (Expr->Type); IsFastcall = (Func->Flags & FD_VARIADIC) == 0 &&
(AutoCDecl ?
IsQualFastcall (Expr->Type) :
!IsQualCDecl (Expr->Type));
} }
/* Parse the parameter list */ /* Parse the parameter list */

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 2000-2012, Ullrich von Bassewitz */ /* (C) 2000-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -460,6 +460,9 @@ void NewFunc (SymEntry* Func)
*/ */
if (D->ParamCount > 0 || (D->Flags & FD_VARIADIC) != 0) { if (D->ParamCount > 0 || (D->Flags & FD_VARIADIC) != 0) {
g_importmainargs (); g_importmainargs ();
/* The start-up code doesn't fast-call main(). */
Func->Type->C |= T_QUAL_CDECL;
} }
/* Determine if this is a main function in a C99 environment that /* Determine if this is a main function in a C99 environment that
@@ -478,13 +481,12 @@ void NewFunc (SymEntry* Func)
PushLiteralPool (Func); PushLiteralPool (Func);
/* If this is a fastcall function, push the last parameter onto the stack */ /* If this is a fastcall function, push the last parameter onto the stack */
if (IsQualFastcall (Func->Type) && D->ParamCount > 0) { if ((D->Flags & FD_VARIADIC) == 0 && D->ParamCount > 0 &&
(AutoCDecl ?
IsQualFastcall (Func->Type) :
!IsQualCDecl (Func->Type))) {
unsigned Flags; unsigned Flags;
/* Fastcall functions may never have an ellipsis or the compiler is buggy */
CHECK ((D->Flags & FD_VARIADIC) == 0);
/* Generate the push */ /* Generate the push */
if (IsTypeFunc (D->LastParam->Type)) { if (IsTypeFunc (D->LastParam->Type)) {
/* Pointer to function */ /* Pointer to function */

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2012, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -44,6 +44,7 @@
unsigned char AddSource = 0; /* Add source lines as comments */ unsigned char AddSource = 0; /* Add source lines as comments */
unsigned char AutoCDecl = 0; /* Make functions default to __cdecl__ */
unsigned char DebugInfo = 0; /* Add debug info to the obj */ unsigned char DebugInfo = 0; /* Add debug info to the obj */
unsigned char PreprocessOnly = 0; /* Just preprocess the input */ unsigned char PreprocessOnly = 0; /* Just preprocess the input */
unsigned char DebugOptOutput = 0; /* Output debug stuff */ unsigned char DebugOptOutput = 0; /* Output debug stuff */

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2012, Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -52,6 +52,7 @@
/* Options */ /* Options */
extern unsigned char AddSource; /* Add source lines as comments */ extern unsigned char AddSource; /* Add source lines as comments */
extern unsigned char AutoCDecl; /* Make functions default to __cdecl__ */
extern unsigned char DebugInfo; /* Add debug info to the obj */ extern unsigned char DebugInfo; /* Add debug info to the obj */
extern unsigned char PreprocessOnly; /* Just preprocess the input */ extern unsigned char PreprocessOnly; /* Just preprocess the input */
extern unsigned char DebugOptOutput; /* Output debug stuff */ extern unsigned char DebugOptOutput; /* Output debug stuff */

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 2000-2013, Ullrich von Bassewitz */ /* (C) 2000-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
@@ -104,6 +104,7 @@ static void Usage (void)
"\n" "\n"
"Long options:\n" "Long options:\n"
" --add-source\t\t\tInclude source as comment\n" " --add-source\t\t\tInclude source as comment\n"
" --all-cdecl\t\t\tMake functions default to __cdecl__\n"
" --bss-name seg\t\tSet the name of the BSS segment\n" " --bss-name seg\t\tSet the name of the BSS segment\n"
" --check-stack\t\t\tGenerate stack overflow checks\n" " --check-stack\t\t\tGenerate stack overflow checks\n"
" --code-name seg\t\tSet the name of the CODE segment\n" " --code-name seg\t\tSet the name of the CODE segment\n"
@@ -350,6 +351,15 @@ static void OptAddSource (const char* Opt attribute ((unused)),
static void OptAllCDecl (const char* Opt attribute ((unused)),
const char* Arg attribute ((unused)))
/* Make functions default to cdecl instead of fastcall. */
{
AutoCDecl = 1;
}
static void OptBssName (const char* Opt attribute ((unused)), const char* Arg) static void OptBssName (const char* Opt attribute ((unused)), const char* Arg)
/* Handle the --bss-name option */ /* Handle the --bss-name option */
{ {
@@ -790,6 +800,7 @@ int main (int argc, char* argv[])
/* Program long options */ /* Program long options */
static const LongOpt OptTab[] = { static const LongOpt OptTab[] = {
{ "--add-source", 0, OptAddSource }, { "--add-source", 0, OptAddSource },
{ "--all-cdecl", 0, OptAllCDecl },
{ "--bss-name", 1, OptBssName }, { "--bss-name", 1, OptBssName },
{ "--check-stack", 0, OptCheckStack }, { "--check-stack", 0, OptCheckStack },
{ "--code-name", 1, OptCodeName }, { "--code-name", 1, OptCodeName },

View File

@@ -6,10 +6,10 @@
/* */ /* */
/* */ /* */
/* */ /* */
/* (C) 1998-2008 Ullrich von Bassewitz */ /* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
/* EMail: uz@cc65.org */ /* EMail: uz@cc65.org */
/* */ /* */
/* */ /* */
/* This software is provided 'as-is', without any expressed or implied */ /* This software is provided 'as-is', without any expressed or implied */
@@ -37,6 +37,7 @@
/* cc65 */ /* cc65 */
#include "funcdesc.h" #include "funcdesc.h"
#include "global.h"
#include "symtab.h" #include "symtab.h"
#include "typecmp.h" #include "typecmp.h"
@@ -245,23 +246,36 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
return; return;
} }
} }
if (LeftType == T_TYPE_FUNC) {
/* If a calling convention wasn't set explicitly,
** then assume the default one.
*/
if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) {
LeftQual |= (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
}
if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) {
RightQual |= (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
}
}
if (LeftQual != RightQual) { if (LeftQual != RightQual) {
/* On the first indirection level, different qualifiers mean /* On the first indirection level, different qualifiers mean
** that the types are still compatible. On the second level, ** that the types still are compatible. On the second level,
** this is a (maybe minor) error, so we create a special ** that is a (maybe minor) error. We create a special return-code
** return code, since a qualifier is dropped from a pointer. ** if a qualifier is dropped from a pointer. But, different calling
** Starting from the next level, the types are incompatible ** conventions are incompatible. Starting from the next level,
** if the qualifiers differ. ** the types are incompatible if the qualifiers differ.
*/ */
/* (Debugging statement) */
/* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */ /* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */
switch (Indirections) { switch (Indirections) {
case 0: case 0:
SetResult (Result, TC_STRICT_COMPATIBLE); SetResult (Result, TC_STRICT_COMPATIBLE);
break; break;
case 1: case 1:
/* A non const value on the right is compatible to a /* A non-const value on the right is compatible to a
** const one to the left, same for volatile. ** const one to the left, same for volatile.
*/ */
if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) || if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) ||
@@ -270,7 +284,11 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
} else { } else {
SetResult (Result, TC_STRICT_COMPATIBLE); SetResult (Result, TC_STRICT_COMPATIBLE);
} }
break;
if (LeftType != T_TYPE_FUNC || (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) {
break;
}
/* else fall through */
default: default:
SetResult (Result, TC_INCOMPATIBLE); SetResult (Result, TC_INCOMPATIBLE);
@@ -280,7 +298,6 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
/* Check for special type elements */ /* Check for special type elements */
switch (LeftType) { switch (LeftType) {
case T_TYPE_PTR: case T_TYPE_PTR:
++Indirections; ++Indirections;
break; break;

View File

@@ -126,7 +126,7 @@ mymain(int argc,char **argv)
} else { } else {
/* why not using a function pointer ? */ /* why not using a function pointer ? */
f = &fact; f = &fact;
print_num((*(long (*)())f)(n), base); print_num((*(long (*)(int))f)(n), base);
} }
printf("\n"); printf("\n");
return 0; return 0;

View File

@@ -26,6 +26,13 @@ TESTS := $(foreach option,. .o. .os. .osi. .osir. .oi. .oir. .or.,$(SOURCES:%.c=
all: $(TESTS) all: $(TESTS)
# cq71.c and cq84.c have "K & R"-style syntax. And, some local forward
# function-declarations don't match the later global function definitions.
# Those programs fail when fastcall is used; but, the cdecl calling convention
# tolerates those conflicts. Therefore, make their functions default to cdecl.
#
$(WORKDIR)/cq71%prg $(WORKDIR)/cq84%prg: CC65FLAGS += -Wc --all-cdecl
$(WORKDIR)/%.prg: %.c $(WORKDIR)/%.prg: %.c
$(CL65) $(CC65FLAGS) $< -o $@ $(CL65) $(CC65FLAGS) $< -o $@
$(SIM65) $(SIM65FLAGS) $@ $(SIM65) $(SIM65FLAGS) $@