diff options
author | Mikko Tiusanen <ams@daug.net> | 2014-05-04 01:18:52 +0300 |
---|---|---|
committer | Mikko Tiusanen <ams@daug.net> | 2014-05-04 01:18:52 +0300 |
commit | 01beb9919b95479d8be040bec74abc5cc67a5e43 (patch) | |
tree | 65f0b79e793848491832756a4c3a32b23668fab3 /src/tools | |
parent | 191d731da136b7ee959a17e63111c9146219a768 (diff) |
Initial import.
Diffstat (limited to 'src/tools')
69 files changed, 24236 insertions, 0 deletions
diff --git a/src/tools/asm/README.Id b/src/tools/asm/README.Id new file mode 100644 index 0000000..adacb50 --- /dev/null +++ b/src/tools/asm/README.Id @@ -0,0 +1,10 @@ +2002-10-25 Timothee Besset <ttimo@idsoftware.com> +If you are looking for a faster version of the q3asm tool, try: +http://www.icculus.org/~phaethon/q3/q3asm-turbo/q3asm-turbo.html + +2001-10-31 Timothee Besset <ttimo@idsoftware.com> +updated from the $/source/q3asm code +modified for portability and use with >= 1.31 mod source release + +the cmdlib.c cmdlib.h mathlib.h qfiles.h have been copied from +$/source/common diff --git a/src/tools/asm/cmdlib.c b/src/tools/asm/cmdlib.c new file mode 100644 index 0000000..bf50346 --- /dev/null +++ b/src/tools/asm/cmdlib.c @@ -0,0 +1,1141 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cmdlib.c + +#include "cmdlib.h" +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef WIN32 +#include <direct.h> +#include <windows.h> +#elif defined(NeXT) +#include <libc.h> +#else +#include <unistd.h> +#endif + +#define BASEDIRNAME "quake" // assumed to have a 2 or 3 following +#define PATHSEPERATOR '/' + +// set these before calling CheckParm +int myargc; +char **myargv; + +char com_token[1024]; +qboolean com_eof; + +qboolean archive; +char archivedir[1024]; + + +/* +=================== +ExpandWildcards + +Mimic unix command line expansion +=================== +*/ +#define MAX_EX_ARGC 1024 +int ex_argc; +char *ex_argv[MAX_EX_ARGC]; +#ifdef _WIN32 +#include "io.h" +void ExpandWildcards( int *argc, char ***argv ) +{ + struct _finddata_t fileinfo; + intptr_t handle; + int i; + char filename[1024]; + char filebase[1024]; + char *path; + + ex_argc = 0; + for (i=0 ; i<*argc ; i++) + { + path = (*argv)[i]; + if ( path[0] == '-' + || ( !strstr(path, "*") && !strstr(path, "?") ) ) + { + ex_argv[ex_argc++] = path; + continue; + } + + handle = _findfirst (path, &fileinfo); + if (handle == -1) + return; + + ExtractFilePath (path, filebase); + + do + { + sprintf (filename, "%s%s", filebase, fileinfo.name); + ex_argv[ex_argc++] = copystring (filename); + } while (_findnext( handle, &fileinfo ) != -1); + + _findclose (handle); + } + + *argc = ex_argc; + *argv = ex_argv; +} +#else +void ExpandWildcards (int *argc, char ***argv) +{ +} +#endif + +#ifdef WIN_ERROR +#include <windows.h> +/* +================= +Error + +For abnormal program terminations in windowed apps +================= +*/ +void Error( const char *error, ... ) +{ + va_list argptr; + char text[1024]; + char text2[1024]; + int err; + + err = GetLastError (); + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + + sprintf (text2, "%s\nGetLastError() = %i", text, err); + MessageBox(NULL, text2, "Error", 0 /* MB_OK */ ); + + exit (1); +} + +#else +/* +================= +Error + +For abnormal program terminations in console apps +================= +*/ +void Error( const char *error, ...) +{ + va_list argptr; + + _printf ("\n************ ERROR ************\n"); + + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + _printf ("\r\n"); + + exit (1); +} +#endif + +// only printf if in verbose mode +qboolean verbose = qfalse; +void qprintf( const char *format, ... ) { + va_list argptr; + + if (!verbose) + return; + + va_start (argptr,format); + vprintf (format,argptr); + va_end (argptr); + +} + +#ifdef WIN32 +HWND hwndOut = NULL; +qboolean lookedForServer = qfalse; +UINT wm_BroadcastCommand = -1; +#endif + +void _printf( const char *format, ... ) { + va_list argptr; + char text[4096]; +#ifdef WIN32 + ATOM a; +#endif + va_start (argptr,format); + vsprintf (text, format, argptr); + va_end (argptr); + + printf("%s", text); + +#ifdef WIN32 + if (!lookedForServer) { + lookedForServer = qtrue; + hwndOut = FindWindow(NULL, "Q3Map Process Server"); + if (hwndOut) { + wm_BroadcastCommand = RegisterWindowMessage( "Q3MPS_BroadcastCommand" ); + } + } + if (hwndOut) { + a = GlobalAddAtom(text); + PostMessage(hwndOut, wm_BroadcastCommand, 0, (LPARAM)a); + } +#endif +} + + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake\ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ + +char qdir[1024]; +char gamedir[1024]; +char writedir[1024]; + +void SetQdirFromPath( const char *path ) +{ + char temp[1024]; + const char *c; + const char *sep; + int len, count; + + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd (temp); + strcat (temp, path); + path = temp; + } + + // search for "quake2" in path + + len = strlen(BASEDIRNAME); + for (c=path+strlen(path)-1 ; c != path ; c--) + { + int i; + + if (!Q_strncasecmp (c, BASEDIRNAME, len)) + { + // + //strncpy (qdir, path, c+len+2-path); + // the +2 assumes a 2 or 3 following quake which is not the + // case with a retail install + // so we need to add up how much to the next separator + sep = c + len; + count = 1; + while (*sep && *sep != '/' && *sep != '\\') + { + sep++; + count++; + } + strncpy (qdir, path, c+len+count-path); + qprintf ("qdir: %s\n", qdir); + for ( i = 0; i < strlen( qdir ); i++ ) + { + if ( qdir[i] == '\\' ) + qdir[i] = '/'; + } + + c += len+count; + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy (gamedir, path, c+1-path); + + for ( i = 0; i < strlen( gamedir ); i++ ) + { + if ( gamedir[i] == '\\' ) + gamedir[i] = '/'; + } + + qprintf ("gamedir: %s\n", gamedir); + + if ( !writedir[0] ) + strcpy( writedir, gamedir ); + else if ( writedir[strlen( writedir )-1] != '/' ) + { + writedir[strlen( writedir )] = '/'; + writedir[strlen( writedir )+1] = 0; + } + + return; + } + c++; + } + Error ("No gamedir in %s", path); + return; + } + } + Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); +} + +char *ExpandArg (const char *path) +{ + static char full[1024]; + + if (path[0] != '/' && path[0] != '\\' && path[1] != ':') + { + Q_getwd (full); + strcat (full, path); + } + else + strcpy (full, path); + return full; +} + +char *ExpandPath (const char *path) +{ + static char full[1024]; + if (!qdir[0]) + Error ("ExpandPath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { + strcpy( full, path ); + return full; + } + sprintf (full, "%s%s", qdir, path); + return full; +} + +char *ExpandGamePath (const char *path) +{ + static char full[1024]; + if (!qdir[0]) + Error ("ExpandGamePath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { + strcpy( full, path ); + return full; + } + sprintf (full, "%s%s", gamedir, path); + return full; +} + +char *ExpandPathAndArchive (const char *path) +{ + char *expanded; + char archivename[1024]; + + expanded = ExpandPath (path); + + if (archive) + { + sprintf (archivename, "%s/%s", archivedir, path); + QCopyFile (expanded, archivename); + } + return expanded; +} + + +char *copystring(const char *s) +{ + char *b; + b = malloc(strlen(s)+1); + strcpy (b, s); + return b; +} + + + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ + time_t t; + + time (&t); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + +void Q_getwd (char *out) +{ + int i = 0; + +#ifdef WIN32 + if (_getcwd (out, 256) == NULL) + strcpy(out, "."); /* shrug */ + strcat (out, "\\"); +#else + if (getcwd (out, 256) == NULL) + strcpy(out, "."); /* shrug */ + strcat (out, "/"); +#endif + + while ( out[i] != 0 ) + { + if ( out[i] == '\\' ) + out[i] = '/'; + i++; + } +} + + +void Q_mkdir (const char *path) +{ +#ifdef WIN32 + if (_mkdir (path) != -1) + return; +#else + if (mkdir (path, 0777) != -1) + return; +#endif + if (errno != EEXIST) + Error ("mkdir %s: %s",path, strerror(errno)); +} + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int FileTime (const char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + com_eof = qtrue; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + + +int Q_strncasecmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + do + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + } while (c1); + + return 0; // strings are equal +} + +int Q_stricmp (const char *s1, const char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + + +char *strupr (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char *strlower (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (const char *check) +{ + int i; + + for (i = 1;i<myargc;i++) + { + if ( !Q_stricmp(check, myargv[i]) ) + return i; + } + + return 0; +} + + + +/* +================ +Q_filelength +================ +*/ +int Q_filelength (FILE *f) +{ + int pos; + int end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + +#ifdef MAX_PATH +#undef MAX_PATH +#endif +#define MAX_PATH 4096 +static FILE* myfopen(const char* filename, const char* mode) +{ + char* p; + char fn[MAX_PATH]; + + fn[0] = '\0'; + strncat(fn, filename, sizeof(fn)-1); + + for(p=fn;*p;++p) if(*p == '\\') *p = '/'; + + return fopen(fn, mode); +} + + +FILE *SafeOpenWrite (const char *filename) +{ + FILE *f; + + f = myfopen(filename, "wb"); + + if (!f) + Error ("Error opening %s: %s",filename,strerror(errno)); + + return f; +} + +FILE *SafeOpenRead (const char *filename) +{ + FILE *f; + + f = myfopen(filename, "rb"); + + if (!f) + Error ("Error opening %s: %s",filename,strerror(errno)); + + return f; +} + + +void SafeRead (FILE *f, void *buffer, int count) +{ + if ( fread (buffer, 1, count, f) != (size_t)count) + Error ("File read failure"); +} + + +void SafeWrite (FILE *f, const void *buffer, int count) +{ + if (fwrite (buffer, 1, count, f) != (size_t)count) + Error ("File write failure"); +} + + +/* +============== +FileExists +============== +*/ +qboolean FileExists (const char *filename) +{ + FILE *f; + + f = myfopen (filename, "r"); + if (!f) + return qfalse; + fclose (f); + return qtrue; +} + +/* +============== +LoadFile +============== +*/ +int LoadFile( const char *filename, void **bufferptr ) +{ + FILE *f; + int length; + void *buffer; + + f = SafeOpenRead (filename); + length = Q_filelength (f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +LoadFileBlock +- +rounds up memory allocation to 4K boundry +- +============== +*/ +int LoadFileBlock( const char *filename, void **bufferptr ) +{ + FILE *f; + int length, nBlock, nAllocSize; + void *buffer; + + f = SafeOpenRead (filename); + length = Q_filelength (f); + nAllocSize = length; + nBlock = nAllocSize % MEM_BLOCKSIZE; + if ( nBlock > 0) { + nAllocSize += MEM_BLOCKSIZE - nBlock; + } + buffer = malloc (nAllocSize+1); + memset(buffer, 0, nAllocSize+1); + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +TryLoadFile + +Allows failure +============== +*/ +int TryLoadFile (const char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + *bufferptr = NULL; + + f = myfopen (filename, "rb"); + if (!f) + return -1; + length = Q_filelength (f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +SaveFile +============== +*/ +void SaveFile (const char *filename, const void *buffer, int count) +{ + FILE *f; + + f = SafeOpenWrite (filename); + SafeWrite (f, buffer, count); + fclose (f); +} + + + +void DefaultExtension (char *path, const char *extension) +{ + char *src; +// +// if path doesnt have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen(path) - 1; + + while (*src != '/' && *src != '\\' && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} + + +void DefaultPath (char *path, const char *basepath) +{ + char temp[128]; + + if (path[0] == PATHSEPERATOR) + return; // absolute path location + strcpy (temp,path); + strcpy (path,basepath); + strcat (path,temp); +} + + +void StripFilename (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +// FIXME: should include the slash, otherwise +// backing to an empty path will be wrong when appending a slash +void ExtractFilePath (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '\\' && *(src-1) != '/') + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (const char *hex) +{ + const char *str; + int num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (const char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +short ShortSwap (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +int LongSwap (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +typedef union { + float f; + unsigned int i; +} _FloatByteUnion; + +float FloatSwap (const float *f) { + _FloatByteUnion out; + + out.f = *f; + out.i = LongSwap(out.i); + + return out.f; +} + +//======================================================= + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + +/* +============ +CreatePath +============ +*/ +void CreatePath (const char *path) +{ + const char *ofs; + char c; + char dir[1024]; + +#ifdef _WIN32 + int olddrive = -1; + + if ( path[1] == ':' ) + { + olddrive = _getdrive(); + _chdrive( toupper( path[0] ) - 'A' + 1 ); + } +#endif + + if (path[1] == ':') + path += 2; + + for (ofs = path+1 ; *ofs ; ofs++) + { + c = *ofs; + if (c == '/' || c == '\\') + { // create the directory + memcpy( dir, path, ofs - path ); + dir[ ofs - path ] = 0; + Q_mkdir( dir ); + } + } + +#ifdef _WIN32 + if ( olddrive != -1 ) + { + _chdrive( olddrive ); + } +#endif +} + + +/* +============ +QCopyFile + + Used to archive source files +============ +*/ +void QCopyFile (const char *from, const char *to) +{ + void *buffer; + int length; + + length = LoadFile (from, &buffer); + CreatePath (to); + SaveFile (to, buffer, length); + free (buffer); +} diff --git a/src/tools/asm/cmdlib.h b/src/tools/asm/cmdlib.h new file mode 100644 index 0000000..20d06de --- /dev/null +++ b/src/tools/asm/cmdlib.h @@ -0,0 +1,153 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#ifdef _MSC_VER +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4305) // truncate from double to float + +#pragma check_stack(off) + +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <stdarg.h> + +#ifdef _MSC_VER + +#pragma intrinsic( memset, memcpy ) + +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum { qfalse, qtrue } qboolean; +typedef unsigned char byte; +#endif + +#define MAX_OS_PATH 1024 +#define MEM_BLOCKSIZE 4096 + +// the dec offsetof macro doesnt work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +char *strupr (char *in); +char *strlower (char *in); +int Q_strncasecmp( const char *s1, const char *s2, int n ); +int Q_stricmp( const char *s1, const char *s2 ); +void Q_getwd( char *out ); + +int Q_filelength (FILE *f); +int FileTime( const char *path ); + +void Q_mkdir( const char *path ); + +extern char qdir[1024]; +extern char gamedir[1024]; +extern char writedir[1024]; +void SetQdirFromPath( const char *path ); +char *ExpandArg( const char *path ); // from cmd line +char *ExpandPath( const char *path ); // from scripts +char *ExpandGamePath (const char *path); +char *ExpandPathAndArchive( const char *path ); + + +double I_FloatTime( void ); + +void Error( const char *error, ... ); +int CheckParm( const char *check ); + +FILE *SafeOpenWrite( const char *filename ); +FILE *SafeOpenRead( const char *filename ); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, const void *buffer, int count); + +int LoadFile( const char *filename, void **bufferptr ); +int LoadFileBlock( const char *filename, void **bufferptr ); +int TryLoadFile( const char *filename, void **bufferptr ); +void SaveFile( const char *filename, const void *buffer, int count ); +qboolean FileExists( const char *filename ); + +void DefaultExtension( char *path, const char *extension ); +void DefaultPath( char *path, const char *basepath ); +void StripFilename( char *path ); +void StripExtension( char *path ); + +void ExtractFilePath( const char *path, char *dest ); +void ExtractFileBase( const char *path, char *dest ); +void ExtractFileExtension( const char *path, char *dest ); + +int ParseNum (const char *str); + +char *COM_Parse (char *data); + +extern char com_token[1024]; +extern qboolean com_eof; + +char *copystring(const char *s); + + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); + +void CreatePath( const char *path ); +void QCopyFile( const char *from, const char *to ); + +extern qboolean archive; +extern char archivedir[1024]; + + +extern qboolean verbose; +void qprintf( const char *format, ... ); +void _printf( const char *format, ... ); + +void ExpandWildcards( int *argc, char ***argv ); + + +// for compression routines +typedef struct +{ + void *data; + int count, width, height; +} cblock_t; + + +#endif diff --git a/src/tools/asm/lib.txt b/src/tools/asm/lib.txt new file mode 100644 index 0000000..737a030 --- /dev/null +++ b/src/tools/asm/lib.txt @@ -0,0 +1,31 @@ + +strlen +strcasecmp +tolower +strcat +strncpy +strcmp +strcpy +strchr + +vsprintf + +memcpy +memset +rand + +atoi +atof + +abs + +floor +fabs +tan +atan +sqrt +log +cos +sin +atan2 + diff --git a/src/tools/asm/mathlib.h b/src/tools/asm/mathlib.h new file mode 100644 index 0000000..bc63a4d --- /dev/null +++ b/src/tools/asm/mathlib.h @@ -0,0 +1,95 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include <math.h> + +#ifdef DOUBLEVEC_T +typedef double vec_t; +#else +typedef float vec_t; +#endif +typedef vec_t vec2_t[3]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define Q_PI 3.14159265358979323846 +#define DEG2RAD( a ) ( ( (a) * Q_PI ) / 180.0F ) +#define RAD2DEG( a ) ( ( (a) * 180.0f ) / Q_PI ) + +extern vec3_t vec3_origin; + +#define EQUAL_EPSILON 0.001 + +// plane types are used to speed some tests +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 +#define PLANE_NON_AXIAL 3 + +qboolean VectorCompare( const vec3_t v1, const vec3_t v2 ); + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];} +#define VectorClear(x) {x[0] = x[1] = x[2] = 0;} +#define VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];} +void Vec10Copy( vec_t *in, vec_t *out ); + +vec_t Q_rint (vec_t in); +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); +void _VectorScale (vec3_t v, vec_t scale, vec3_t out); + +double VectorLength( const vec3_t v ); + +void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc ); + +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); +vec_t VectorNormalize( const vec3_t in, vec3_t out ); +vec_t ColorNormalize( const vec3_t in, vec3_t out ); +void VectorInverse (vec3_t v); + +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); + +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); + +void NormalToLatLong( const vec3_t normal, byte bytes[2] ); + +int PlaneTypeForNormal (vec3_t normal); + +#endif diff --git a/src/tools/asm/notes.txt b/src/tools/asm/notes.txt new file mode 100644 index 0000000..63297f3 --- /dev/null +++ b/src/tools/asm/notes.txt @@ -0,0 +1,16 @@ + +don't do any paramter conversion (double to float, etc) + + + +Why? + +Security. +Portability. + +It may be more aproachable. + +can still use regular dlls for development purposes + +lcc +q3asm diff --git a/src/tools/asm/ops.txt b/src/tools/asm/ops.txt new file mode 100644 index 0000000..e897f49 --- /dev/null +++ b/src/tools/asm/ops.txt @@ -0,0 +1,132 @@ +CNSTF, +CNSTI, +CNSTP, +CNSTU, + +ARGB, +ARGF, +ARGI, +ARGP, +ARGU, + +ASGNB, +ASGNF, +ASGNI, +ASGNP, +ASGNU, + +INDIRB, +INDIRF, +INDIRI, +INDIRP, +INDIRU, + +CVFF, +CVFI, + +CVIF, +CVII, +CVIU, + +CVPU, + +CVUI, +CVUP, +CVUU, + +NEGF, +NEGI, + +CALLB, +CALLF, +CALLI, +CALLP, +CALLU, +CALLV, + +RETF, +RETI, +RETP, +RETU, +RETV, + +ADDRGP, + +ADDRFP, + +ADDRLP, + +ADDF, +ADDI, +ADDP, +ADDU, + +SUBF, +SUBI, +SUBP, +SUBU, + +LSHI, +LSHU, + +MODI, +MODU, + +RSHI, +RSHU, + +BANDI, +BANDU, + +BCOMI, +BCOMU, + +BORI, +BORU, + +BXORI, +BXORU, + +DIVF, +DIVI, +DIVU, + +MULF, +MULI, +MULU, + +EQF, +EQI, +EQU, + +GEF, +GEI, +GEU, + +GTF, +GTI, +GTU, + +LEF, +LEI, +LEU, + +LTF, +LTI, +LTU, + +NEF, +NEI, +NEU, + +JUMPV, + +LABELV, + +LOADB, +LOADF, +LOADI, +LOADP, +LOADU, + + diff --git a/src/tools/asm/opstrings.h b/src/tools/asm/opstrings.h new file mode 100644 index 0000000..d2c3210 --- /dev/null +++ b/src/tools/asm/opstrings.h @@ -0,0 +1,176 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +{ "BREAK", OP_BREAK }, + +{ "CNSTF4", OP_CONST }, +{ "CNSTI4", OP_CONST }, +{ "CNSTP4", OP_CONST }, +{ "CNSTU4", OP_CONST }, + +{ "CNSTI2", OP_CONST }, +{ "CNSTU2", OP_CONST }, + +{ "CNSTI1", OP_CONST }, +{ "CNSTU1", OP_CONST }, + +//{ "ARGB", OP_ARG }, +//{ "ARGF", OP_ARG }, +//{ "ARGI", OP_ARG }, +//{ "ARGP", OP_ARG }, +//{ "ARGU", OP_ARG }, + +{ "ASGNB", OP_BLOCK_COPY }, +{ "ASGNF4", OP_STORE4 }, +{ "ASGNI4", OP_STORE4 }, +{ "ASGNP4", OP_STORE4 }, +{ "ASGNU4", OP_STORE4 }, + +{ "ASGNI2", OP_STORE2 }, +{ "ASGNU2", OP_STORE2 }, + +{ "ASGNI1", OP_STORE1 }, +{ "ASGNU1", OP_STORE1 }, + +{ "INDIRB", OP_IGNORE }, // block copy deals with this +{ "INDIRF4", OP_LOAD4 }, +{ "INDIRI4", OP_LOAD4 }, +{ "INDIRP4", OP_LOAD4 }, +{ "INDIRU4", OP_LOAD4 }, + +{ "INDIRI2", OP_LOAD2 }, +{ "INDIRU2", OP_LOAD2 }, + +{ "INDIRI1", OP_LOAD1 }, +{ "INDIRU1", OP_LOAD1 }, + +{ "CVFF4", OP_UNDEF }, +{ "CVFI4", OP_CVFI }, + +{ "CVIF4", OP_CVIF }, +{ "CVII4", OP_SEX8 }, // will be either SEX8 or SEX16 +{ "CVII1", OP_IGNORE }, +{ "CVII2", OP_IGNORE }, +{ "CVIU4", OP_IGNORE }, + +{ "CVPU4", OP_IGNORE }, + +{ "CVUI4", OP_IGNORE }, +{ "CVUP4", OP_IGNORE }, +{ "CVUU4", OP_IGNORE }, + +{ "CVUU1", OP_IGNORE }, + +{ "NEGF4", OP_NEGF }, +{ "NEGI4", OP_NEGI }, + +//{ "CALLB", OP_UNDEF }, +//{ "CALLF", OP_UNDEF }, +//{ "CALLI", OP_UNDEF }, +//{ "CALLP", OP_UNDEF }, +//{ "CALLU", OP_UNDEF }, +//{ "CALLV", OP_CALL }, + +//{ "RETF", OP_UNDEF }, +//{ "RETI", OP_UNDEF }, +//{ "RETP", OP_UNDEF }, +//{ "RETU", OP_UNDEF }, +//{ "RETV", OP_UNDEF }, + +{ "ADDRGP4", OP_CONST }, + +//{ "ADDRFP", OP_PARM }, +//{ "ADDRLP", OP_LOCAL }, + +{ "ADDF4", OP_ADDF }, +{ "ADDI4", OP_ADD }, +{ "ADDP4", OP_ADD }, +{ "ADDP", OP_ADD }, +{ "ADDU4", OP_ADD }, + +{ "SUBF4", OP_SUBF }, +{ "SUBI4", OP_SUB }, +{ "SUBP4", OP_SUB }, +{ "SUBU4", OP_SUB }, + +{ "LSHI4", OP_LSH }, +{ "LSHU4", OP_LSH }, + +{ "MODI4", OP_MODI }, +{ "MODU4", OP_MODU }, + +{ "RSHI4", OP_RSHI }, +{ "RSHU4", OP_RSHU }, + +{ "BANDI4", OP_BAND }, +{ "BANDU4", OP_BAND }, + +{ "BCOMI4", OP_BCOM }, +{ "BCOMU4", OP_BCOM }, + +{ "BORI4", OP_BOR }, +{ "BORU4", OP_BOR }, + +{ "BXORI4", OP_BXOR }, +{ "BXORU4", OP_BXOR }, + +{ "DIVF4", OP_DIVF }, +{ "DIVI4", OP_DIVI }, +{ "DIVU4", OP_DIVU }, + +{ "MULF4", OP_MULF }, +{ "MULI4", OP_MULI }, +{ "MULU4", OP_MULU }, + +{ "EQF4", OP_EQF }, +{ "EQI4", OP_EQ }, +{ "EQU4", OP_EQ }, + +{ "GEF4", OP_GEF }, +{ "GEI4", OP_GEI }, +{ "GEU4", OP_GEU }, + +{ "GTF4", OP_GTF }, +{ "GTI4", OP_GTI }, +{ "GTU4", OP_GTU }, + +{ "LEF4", OP_LEF }, +{ "LEI4", OP_LEI }, +{ "LEU4", OP_LEU }, + +{ "LTF4", OP_LTF }, +{ "LTI4", OP_LTI }, +{ "LTU4", OP_LTU }, + +{ "NEF4", OP_NEF }, +{ "NEI4", OP_NE }, +{ "NEU4", OP_NE }, + +{ "JUMPV", OP_JUMP }, + +{ "LOADB4", OP_UNDEF }, +{ "LOADF4", OP_UNDEF }, +{ "LOADI4", OP_UNDEF }, +{ "LOADP4", OP_UNDEF }, +{ "LOADU4", OP_UNDEF }, + + diff --git a/src/tools/asm/q3asm.c b/src/tools/asm/q3asm.c new file mode 100644 index 0000000..c42b9ac --- /dev/null +++ b/src/tools/asm/q3asm.c @@ -0,0 +1,1647 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../../qcommon/q_platform.h" +#include "cmdlib.h" +#include "mathlib.h" +#include "../../qcommon/qfiles.h" + +/* 19079 total symbols in FI, 2002 Jan 23 */ +#define DEFAULT_HASHTABLE_SIZE 2048 + +char outputFilename[MAX_OS_PATH]; + +// the zero page size is just used for detecting run time faults +#define ZERO_PAGE_SIZE 0 // 256 + +typedef enum { + OP_UNDEF, + + OP_IGNORE, + + OP_BREAK, + + OP_ENTER, + OP_LEAVE, + OP_CALL, + OP_PUSH, + OP_POP, + + OP_CONST, + OP_LOCAL, + + OP_JUMP, + + //------------------- + + OP_EQ, + OP_NE, + + OP_LTI, + OP_LEI, + OP_GTI, + OP_GEI, + + OP_LTU, + OP_LEU, + OP_GTU, + OP_GEU, + + OP_EQF, + OP_NEF, + + OP_LTF, + OP_LEF, + OP_GTF, + OP_GEF, + + //------------------- + + OP_LOAD1, + OP_LOAD2, + OP_LOAD4, + OP_STORE1, + OP_STORE2, + OP_STORE4, // *(stack[top-1]) = stack[yop + OP_ARG, + OP_BLOCK_COPY, + + //------------------- + + OP_SEX8, + OP_SEX16, + + OP_NEGI, + OP_ADD, + OP_SUB, + OP_DIVI, + OP_DIVU, + OP_MODI, + OP_MODU, + OP_MULI, + OP_MULU, + + OP_BAND, + OP_BOR, + OP_BXOR, + OP_BCOM, + + OP_LSH, + OP_RSHI, + OP_RSHU, + + OP_NEGF, + OP_ADDF, + OP_SUBF, + OP_DIVF, + OP_MULF, + + OP_CVIF, + OP_CVFI +} opcode_t; + +typedef struct { + int imageBytes; // after decompression + int entryPoint; + int stackBase; + int stackSize; +} executableHeader_t; + +typedef enum { + CODESEG, + DATASEG, // initialized 32 bit data, will be byte swapped + LITSEG, // strings + BSSSEG, // 0 filled + JTRGSEG, // pseudo-segment that contains only jump table targets + NUM_SEGMENTS +} segmentName_t; + +#define MAX_IMAGE 0x400000 + +typedef struct { + byte image[MAX_IMAGE]; + int imageUsed; + int segmentBase; // only valid on second pass +} segment_t; + +typedef struct symbol_s { + struct symbol_s *next; + int hash; + segment_t *segment; + char *name; + int value; +} symbol_t; + +typedef struct hashchain_s { + void *data; + struct hashchain_s *next; +} hashchain_t; + +typedef struct hashtable_s { + int buckets; + hashchain_t **table; +} hashtable_t; + +int symtablelen = DEFAULT_HASHTABLE_SIZE; +hashtable_t *symtable; +hashtable_t *optable; + +segment_t segment[NUM_SEGMENTS]; +segment_t *currentSegment; + +int passNumber; + +int numSymbols; +int errorCount; + +typedef struct options_s { + qboolean verbose; + qboolean writeMapFile; + qboolean vanillaQ3Compatibility; +} options_t; + +options_t options = { 0 }; + +symbol_t *symbols; +symbol_t *lastSymbol = 0; /* Most recent symbol defined. */ + + +#define MAX_ASM_FILES 256 +int numAsmFiles; +char *asmFiles[MAX_ASM_FILES]; +char *asmFileNames[MAX_ASM_FILES]; + +int currentFileIndex; +char *currentFileName; +int currentFileLine; + +//int stackSize = 16384; +int stackSize = 0x10000; + +// we need to convert arg and ret instructions to +// stores to the local stack frame, so we need to track the +// characteristics of the current functions stack frame +int currentLocals; // bytes of locals needed by this function +int currentArgs; // bytes of largest argument list called from this function +int currentArgOffset; // byte offset in currentArgs to store next arg, reset each call + +#define MAX_LINE_LENGTH 1024 +char lineBuffer[MAX_LINE_LENGTH]; +int lineParseOffset; +char token[MAX_LINE_LENGTH]; + +int instructionCount; + +typedef struct { + char *name; + int opcode; +} sourceOps_t; + +sourceOps_t sourceOps[] = { +#include "opstrings.h" +}; + +#define NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) ) + +int opcodesHash[ NUM_SOURCE_OPS ]; + + + +static int vreport (const char* fmt, va_list vp) +{ + if (options.verbose != qtrue) + return 0; + return vprintf(fmt, vp); +} + +static int report (const char *fmt, ...) +{ + va_list va; + int retval; + + va_start(va, fmt); + retval = vreport(fmt, va); + va_end(va); + return retval; +} + +/* The chain-and-bucket hash table. -PH */ + +static void hashtable_init (hashtable_t *H, int buckets) +{ + H->buckets = buckets; + H->table = calloc(H->buckets, sizeof(*(H->table))); + return; +} + +static hashtable_t *hashtable_new (int buckets) +{ + hashtable_t *H; + + H = malloc(sizeof(hashtable_t)); + hashtable_init(H, buckets); + return H; +} + +/* No destroy/destructor. No need. */ + +static void hashtable_add (hashtable_t *H, int hashvalue, void *datum) +{ + hashchain_t *hc, **hb; + + hashvalue = (abs(hashvalue) % H->buckets); + hb = &(H->table[hashvalue]); + if (*hb == 0) + { + /* Empty bucket. Create new one. */ + *hb = calloc(1, sizeof(**hb)); + hc = *hb; + } + else + { + /* Get hc to point to last node in chain. */ + for (hc = *hb; hc && hc->next; hc = hc->next); + hc->next = calloc(1, sizeof(*hc)); + hc = hc->next; + } + hc->data = datum; + hc->next = 0; + return; +} + +static hashchain_t *hashtable_get (hashtable_t *H, int hashvalue) +{ + hashvalue = (abs(hashvalue) % H->buckets); + return (H->table[hashvalue]); +} + +static void hashtable_stats (hashtable_t *H) +{ + int len, empties, longest, nodes; + int i; + float meanlen; + hashchain_t *hc; + + report("Stats for hashtable %08X", H); + empties = 0; + longest = 0; + nodes = 0; + for (i = 0; i < H->buckets; i++) + { + if (H->table[i] == 0) + { empties++; continue; } + for (hc = H->table[i], len = 0; hc; hc = hc->next, len++); + if (len > longest) { longest = len; } + nodes += len; + } + meanlen = (float)(nodes) / (H->buckets - empties); +#if 0 +/* Long stats display */ + report(" Total buckets: %d\n", H->buckets); + report(" Total stored nodes: %d\n", nodes); + report(" Longest chain: %d\n", longest); + report(" Empty chains: %d\n", empties); + report(" Mean non-empty chain length: %f\n", meanlen); +#else //0 +/* Short stats display */ + report(", %d buckets, %d nodes", H->buckets, nodes); + report("\n"); + report(" Longest chain: %d, empty chains: %d, mean non-empty: %f", longest, empties, meanlen); +#endif //0 + report("\n"); +} + + +/* Kludge. */ +/* Check if symbol already exists. */ +/* Returns 0 if symbol does NOT already exist, non-zero otherwise. */ +static int hashtable_symbol_exists (hashtable_t *H, int hash, char *sym) +{ + hashchain_t *hc; + symbol_t *s; + + hash = (abs(hash) % H->buckets); + hc = H->table[hash]; + if (hc == 0) + { + /* Empty chain means this symbol has not yet been defined. */ + return 0; + } + for (; hc; hc = hc->next) + { + s = (symbol_t*)hc->data; +// if ((hash == s->hash) && (strcmp(sym, s->name) == 0)) +/* We _already_ know the hash is the same. That's why we're probing! */ + if (strcmp(sym, s->name) == 0) + { + /* Symbol collisions -- symbol already exists. */ + return 1; + } + } + return 0; /* Can't find collision. */ +} + + + + +/* Comparator function for quicksorting. */ +static int symlist_cmp (const void *e1, const void *e2) +{ + const symbol_t *a, *b; + + a = *(const symbol_t **)e1; + b = *(const symbol_t **)e2; +//crumb("Symbol comparison (1) %d to (2) %d\n", a->value, b->value); + return ( a->value - b->value); +} + +/* + Sort the symbols list by using QuickSort (qsort()). + This may take a LOT of memory (a few megabytes?), but memory is cheap these days. + However, qsort(3) already exists, and I'm really lazy. + -PH +*/ +static void sort_symbols () +{ + int i, elems; + symbol_t *s; + symbol_t **symlist; + +//crumb("sort_symbols: Constructing symlist array\n"); + for (elems = 0, s = symbols; s; s = s->next, elems++) /* nop */ ; + symlist = malloc(elems * sizeof(symbol_t*)); + for (i = 0, s = symbols; s; s = s->next, i++) + { + symlist[i] = s; + } +//crumbf("sort_symbols: Quick-sorting %d symbols\n", elems); + qsort(symlist, elems, sizeof(symbol_t*), symlist_cmp); +//crumbf("sort_symbols: Reconstructing symbols list\n"); + s = symbols = symlist[0]; + for (i = 1; i < elems; i++) + { + s->next = symlist[i]; + s = s->next; + } + lastSymbol = s; + s->next = 0; +//crumbf("sort_symbols: verifying..."); fflush(stdout); + for (i = 0, s = symbols; s; s = s->next, i++) /*nop*/ ; +//crumbf(" %d elements\n", i); + free(symlist); /* d'oh. no gc. */ +} + + +#ifdef _MSC_VER +#define INT64 __int64 +#define atoi64 _atoi64 +#else +#define INT64 long long int +#define atoi64 atoll +#endif + +/* + Problem: + BYTE values are specified as signed decimal string. A properly functional + atoip() will cap large signed values at 0x7FFFFFFF. Negative word values are + often specified as very large decimal values by lcc. Therefore, values that + should be between 0x7FFFFFFF and 0xFFFFFFFF come out as 0x7FFFFFFF when using + atoi(). Bad. + + This function is one big evil hack to work around this problem. +*/ +static int atoiNoCap (const char *s) +{ + INT64 l; + union { + unsigned int u; + signed int i; + } retval; + + l = atoi64(s); + /* Now smash to signed 32 bits accordingly. */ + if (l < 0) { + retval.i = (int)l; + } else { + retval.u = (unsigned int)l; + } + return retval.i; /* <- union hackage. I feel dirty with this. -PH */ +} + + + +/* +============= +HashString +============= +*/ +/* Default hash function of Kazlib 1.19, slightly modified. */ +static unsigned int HashString (const char *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const char *str = key; + unsigned int acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return abs(acc); +} + + +/* +============ +CodeError +============ +*/ +static void CodeError( char *fmt, ... ) { + va_list argptr; + + errorCount++; + + fprintf( stderr, "%s:%i ", currentFileName, currentFileLine ); + + va_start( argptr,fmt ); + vfprintf( stderr, fmt, argptr ); + va_end( argptr ); +} + +/* +============ +EmitByte +============ +*/ +static void EmitByte( segment_t *seg, int v ) { + if ( seg->imageUsed >= MAX_IMAGE ) { + Error( "MAX_IMAGE" ); + } + seg->image[ seg->imageUsed ] = v; + seg->imageUsed++; +} + +/* +============ +EmitInt +============ +*/ +static void EmitInt( segment_t *seg, int v ) { + if ( seg->imageUsed >= MAX_IMAGE - 4) { + Error( "MAX_IMAGE" ); + } + seg->image[ seg->imageUsed ] = v & 255; + seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255; + seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255; + seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255; + seg->imageUsed += 4; +} + +/* +============ +DefineSymbol + +Symbols can only be defined on pass 0 +============ +*/ +static void DefineSymbol( char *sym, int value ) { + /* Hand optimization by PhaethonH */ + symbol_t *s; + char expanded[MAX_LINE_LENGTH]; + int hash; + + if ( passNumber == 1 ) { + return; + } + + // add the file prefix to local symbols to guarantee unique + if ( sym[0] == '$' ) { + sprintf( expanded, "%s_%i", sym, currentFileIndex ); + sym = expanded; + } + + hash = HashString( sym ); + + if (hashtable_symbol_exists(symtable, hash, sym)) { + CodeError( "Multiple definitions for %s\n", sym ); + return; + } + + s = malloc( sizeof( *s ) ); + s->next = NULL; + s->name = copystring( sym ); + s->hash = hash; + s->value = value; + s->segment = currentSegment; + + hashtable_add(symtable, hash, s); + +/* + Hash table lookup already speeds up symbol lookup enormously. + We postpone sorting until end of pass 0. + Since we're not doing the insertion sort, lastSymbol should always + wind up pointing to the end of list. + This allows constant time for adding to the list. + -PH +*/ + if (symbols == 0) { + lastSymbol = symbols = s; + } else { + lastSymbol->next = s; + lastSymbol = s; + } +} + + +/* +============ +LookupSymbol + +Symbols can only be evaluated on pass 1 +============ +*/ +static int LookupSymbol( char *sym ) { + symbol_t *s; + char expanded[MAX_LINE_LENGTH]; + int hash; + hashchain_t *hc; + + if ( passNumber == 0 ) { + return 0; + } + + // add the file prefix to local symbols to guarantee unique + if ( sym[0] == '$' ) { + sprintf( expanded, "%s_%i", sym, currentFileIndex ); + sym = expanded; + } + + hash = HashString( sym ); + +/* + Hand optimization by PhaethonH + + Using a hash table with chain/bucket for lookups alone sped up q3asm by almost 3x for me. + -PH +*/ + for (hc = hashtable_get(symtable, hash); hc; hc = hc->next) { + s = (symbol_t*)hc->data; /* ugly typecasting, but it's fast! */ + if ( (hash == s->hash) && !strcmp(sym, s->name) ) { + return s->segment->segmentBase + s->value; + } + } + + CodeError( "error: symbol %s undefined\n", sym ); + passNumber = 0; + DefineSymbol( sym, 0 ); // so more errors aren't printed + passNumber = 1; + return 0; +} + + +/* +============== +ExtractLine + +Extracts the next line from the given text block. +If a full line isn't parsed, returns NULL +Otherwise returns the updated parse pointer +=============== +*/ +static char *ExtractLine( char *data ) { +/* Goal: + Given a string `data', extract one text line into buffer `lineBuffer' that + is no longer than MAX_LINE_LENGTH characters long. Return value is + remainder of `data' that isn't part of `lineBuffer'. + -PH +*/ + /* Hand-optimized by PhaethonH */ + char *p, *q; + + currentFileLine++; + + lineParseOffset = 0; + token[0] = 0; + *lineBuffer = 0; + + p = q = data; + if (!*q) { + return NULL; + } + + for ( ; !((*p == 0) || (*p == '\n')); p++) /* nop */ ; + + if ((p - q) >= MAX_LINE_LENGTH) { + CodeError( "MAX_LINE_LENGTH" ); + return data; + } + + memcpy( lineBuffer, data, (p - data) ); + lineBuffer[(p - data)] = 0; + p += (*p == '\n') ? 1 : 0; /* Skip over final newline. */ + return p; +} + + +/* +============== +Parse + +Parse a token out of linebuffer +============== +*/ +static qboolean Parse( void ) { + /* Hand-optimized by PhaethonH */ + const char *p, *q; + + /* Because lineParseOffset is only updated just before exit, this makes this code version somewhat harder to debug under a symbolic debugger. */ + + *token = 0; /* Clear token. */ + + // skip whitespace + for (p = lineBuffer + lineParseOffset; *p && (*p <= ' '); p++) /* nop */ ; + + // skip ; comments + /* die on end-of-string */ + if ((*p == ';') || (*p == 0)) { + lineParseOffset = p - lineBuffer; + return qfalse; + } + + q = p; /* Mark the start of token. */ + /* Find separator first. */ + for ( ; *p > 32; p++) /* nop */ ; /* XXX: unsafe assumptions. */ + /* *p now sits on separator. Mangle other values accordingly. */ + strncpy(token, q, p - q); + token[p - q] = 0; + + lineParseOffset = p - lineBuffer; + + return qtrue; +} + + +/* +============== +ParseValue +============== +*/ +static int ParseValue( void ) { + Parse(); + return atoiNoCap( token ); +} + + +/* +============== +ParseExpression +============== +*/ +static int ParseExpression(void) { + /* Hand optimization, PhaethonH */ + int i, j; + char sym[MAX_LINE_LENGTH]; + int v; + + /* Skip over a leading minus. */ + for ( i = ((token[0] == '-') ? 1 : 0) ; i < MAX_LINE_LENGTH ; i++ ) { + if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) { + break; + } + } + + memcpy( sym, token, i ); + sym[i] = 0; + + switch (*sym) { /* Resolve depending on first character. */ +/* Optimizing compilers can convert cases into "calculated jumps". I think these are faster. -PH */ + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + v = atoiNoCap(sym); + break; + default: + v = LookupSymbol(sym); + break; + } + + // parse add / subtract offsets + while ( token[i] != 0 ) { + for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) { + if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) { + break; + } + } + + memcpy( sym, token+i+1, j-i-1 ); + sym[j-i-1] = 0; + + switch (token[i]) { + case '+': + v += atoiNoCap(sym); + break; + case '-': + v -= atoiNoCap(sym); + break; + } + + i = j; + } + + return v; +} + + +/* +============== +HackToSegment + +BIG HACK: I want to put all 32 bit values in the data +segment so they can be byte swapped, and all char data in the lit +segment, but switch jump tables are emited in the lit segment and +initialized strng variables are put in the data segment. + +I can change segments here, but I also need to fixup the +label that was just defined + +Note that the lit segment is read-write in the VM, so strings +aren't read only as in some architectures. +============== +*/ +static void HackToSegment( segmentName_t seg ) { + if ( currentSegment == &segment[seg] ) { + return; + } + + currentSegment = &segment[seg]; + if ( passNumber == 0 ) { + lastSymbol->segment = currentSegment; + lastSymbol->value = currentSegment->imageUsed; + } +} + + + + + + + +//#define STAT(L) report("STAT " L "\n"); +#define STAT(L) +#define ASM(O) int TryAssemble##O () + + +/* + These clauses were moved out from AssembleLine() to allow reordering of if's. + An optimizing compiler should reconstruct these back into inline code. + -PH +*/ + + // call instructions reset currentArgOffset +ASM(CALL) +{ + if ( !strncmp( token, "CALL", 4 ) ) { +STAT("CALL"); + EmitByte( &segment[CODESEG], OP_CALL ); + instructionCount++; + currentArgOffset = 0; + return 1; + } + return 0; +} + + // arg is converted to a reversed store +ASM(ARG) +{ + if ( !strncmp( token, "ARG", 3 ) ) { +STAT("ARG"); + EmitByte( &segment[CODESEG], OP_ARG ); + instructionCount++; + if ( 8 + currentArgOffset >= 256 ) { + CodeError( "currentArgOffset >= 256" ); + return 1; + } + EmitByte( &segment[CODESEG], 8 + currentArgOffset ); + currentArgOffset += 4; + return 1; + } + return 0; +} + + // ret just leaves something on the op stack +ASM(RET) +{ + if ( !strncmp( token, "RET", 3 ) ) { +STAT("RET"); + EmitByte( &segment[CODESEG], OP_LEAVE ); + instructionCount++; + EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); + return 1; + } + return 0; +} + + // pop is needed to discard the return value of + // a function +ASM(POP) +{ + if ( !strncmp( token, "pop", 3 ) ) { +STAT("POP"); + EmitByte( &segment[CODESEG], OP_POP ); + instructionCount++; + return 1; + } + return 0; +} + + // address of a parameter is converted to OP_LOCAL +ASM(ADDRF) +{ + int v; + if ( !strncmp( token, "ADDRF", 5 ) ) { +STAT("ADDRF"); + instructionCount++; + Parse(); + v = ParseExpression(); + v = 16 + currentArgs + currentLocals + v; + EmitByte( &segment[CODESEG], OP_LOCAL ); + EmitInt( &segment[CODESEG], v ); + return 1; + } + return 0; +} + + // address of a local is converted to OP_LOCAL +ASM(ADDRL) +{ + int v; + if ( !strncmp( token, "ADDRL", 5 ) ) { +STAT("ADDRL"); + instructionCount++; + Parse(); + v = ParseExpression(); + v = 8 + currentArgs + v; + EmitByte( &segment[CODESEG], OP_LOCAL ); + EmitInt( &segment[CODESEG], v ); + return 1; + } + return 0; +} + +ASM(PROC) +{ + char name[1024]; + if ( !strcmp( token, "proc" ) ) { +STAT("PROC"); + Parse(); // function name + strcpy( name, token ); + + DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed ); + + currentLocals = ParseValue(); // locals + currentLocals = ( currentLocals + 3 ) & ~3; + currentArgs = ParseValue(); // arg marshalling + currentArgs = ( currentArgs + 3 ) & ~3; + + if ( 8 + currentLocals + currentArgs >= 32767 ) { + CodeError( "Locals > 32k in %s\n", name ); + } + + instructionCount++; + EmitByte( &segment[CODESEG], OP_ENTER ); + EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); + return 1; + } + return 0; +} + + +ASM(ENDPROC) +{ + int v, v2; + if ( !strcmp( token, "endproc" ) ) { +STAT("ENDPROC"); + Parse(); // skip the function name + v = ParseValue(); // locals + v2 = ParseValue(); // arg marshalling + + // all functions must leave something on the opstack + instructionCount++; + EmitByte( &segment[CODESEG], OP_PUSH ); + + instructionCount++; + EmitByte( &segment[CODESEG], OP_LEAVE ); + EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); + + return 1; + } + return 0; +} + + +ASM(ADDRESS) +{ + int v; + if ( !strcmp( token, "address" ) ) { +STAT("ADDRESS"); + Parse(); + v = ParseExpression(); + +/* Addresses are 32 bits wide, and therefore go into data segment. */ + HackToSegment( DATASEG ); + EmitInt( currentSegment, v ); + if( passNumber == 1 && token[ 0 ] == '$' ) // crude test for labels + EmitInt( &segment[ JTRGSEG ], v ); + return 1; + } + return 0; +} + +ASM(EXPORT) +{ + if ( !strcmp( token, "export" ) ) { +STAT("EXPORT"); + return 1; + } + return 0; +} + +ASM(IMPORT) +{ + if ( !strcmp( token, "import" ) ) { +STAT("IMPORT"); + return 1; + } + return 0; +} + +ASM(CODE) +{ + if ( !strcmp( token, "code" ) ) { +STAT("CODE"); + currentSegment = &segment[CODESEG]; + return 1; + } + return 0; +} + +ASM(BSS) +{ + if ( !strcmp( token, "bss" ) ) { +STAT("BSS"); + currentSegment = &segment[BSSSEG]; + return 1; + } + return 0; +} + +ASM(DATA) +{ + if ( !strcmp( token, "data" ) ) { +STAT("DATA"); + currentSegment = &segment[DATASEG]; + return 1; + } + return 0; +} + +ASM(LIT) +{ + if ( !strcmp( token, "lit" ) ) { +STAT("LIT"); + currentSegment = &segment[LITSEG]; + return 1; + } + return 0; +} + +ASM(LINE) +{ + if ( !strcmp( token, "line" ) ) { +STAT("LINE"); + return 1; + } + return 0; +} + +ASM(FILE) +{ + if ( !strcmp( token, "file" ) ) { +STAT("FILE"); + return 1; + } + return 0; +} + +ASM(EQU) +{ + char name[1024]; + if ( !strcmp( token, "equ" ) ) { +STAT("EQU"); + Parse(); + strcpy( name, token ); + Parse(); + DefineSymbol( name, atoiNoCap(token) ); + return 1; + } + return 0; +} + +ASM(ALIGN) +{ + int v; + if ( !strcmp( token, "align" ) ) { +STAT("ALIGN"); + v = ParseValue(); + currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 ); + return 1; + } + return 0; +} + +ASM(SKIP) +{ + int v; + if ( !strcmp( token, "skip" ) ) { +STAT("SKIP"); + v = ParseValue(); + currentSegment->imageUsed += v; + return 1; + } + return 0; +} + +ASM(BYTE) +{ + int i, v, v2; + if ( !strcmp( token, "byte" ) ) { +STAT("BYTE"); + v = ParseValue(); + v2 = ParseValue(); + + if ( v == 1 ) { +/* Character (1-byte) values go into lit(eral) segment. */ + HackToSegment( LITSEG ); + } else if ( v == 4 ) { +/* 32-bit (4-byte) values go into data segment. */ + HackToSegment( DATASEG ); + } else if ( v == 2 ) { +/* and 16-bit (2-byte) values will cause q3asm to barf. */ + CodeError( "16 bit initialized data not supported" ); + } + + // emit little endien + for ( i = 0 ; i < v ; i++ ) { + EmitByte( currentSegment, (v2 & 0xFF) ); /* paranoid ANDing -PH */ + v2 >>= 8; + } + return 1; + } + return 0; +} + + // code labels are emited as instruction counts, not byte offsets, + // because the physical size of the code will change with + // different run time compilers and we want to minimize the + // size of the required translation table +ASM(LABEL) +{ + if ( !strncmp( token, "LABEL", 5 ) ) { +STAT("LABEL"); + Parse(); + if ( currentSegment == &segment[CODESEG] ) { + DefineSymbol( token, instructionCount ); + } else { + DefineSymbol( token, currentSegment->imageUsed ); + } + return 1; + } + return 0; +} + + + +/* +============== +AssembleLine + +============== +*/ +static void AssembleLine( void ) { + hashchain_t *hc; + sourceOps_t *op; + int i; + int hash; + + Parse(); + if ( !token[0] ) { + return; + } + + hash = HashString( token ); + +/* + Opcode search using hash table. + Since the opcodes stays mostly fixed, this may benefit even more from a tree. + Always with the tree :) + -PH +*/ + for (hc = hashtable_get(optable, hash); hc; hc = hc->next) { + op = (sourceOps_t*)(hc->data); + i = op - sourceOps; + if ((hash == opcodesHash[i]) && (!strcmp(token, op->name))) { + int opcode; + int expression; + + if ( op->opcode == OP_UNDEF ) { + CodeError( "Undefined opcode: %s\n", token ); + } + if ( op->opcode == OP_IGNORE ) { + return; // we ignore most conversions + } + + // sign extensions need to check next parm + opcode = op->opcode; + if ( opcode == OP_SEX8 ) { + Parse(); + if ( token[0] == '1' ) { + opcode = OP_SEX8; + } else if ( token[0] == '2' ) { + opcode = OP_SEX16; + } else { + CodeError( "Bad sign extension: %s\n", token ); + return; + } + } + + // check for expression + Parse(); + if ( token[0] && op->opcode != OP_CVIF + && op->opcode != OP_CVFI ) { + expression = ParseExpression(); + + // code like this can generate non-dword block copies: + // auto char buf[2] = " "; + // we are just going to round up. This might conceivably + // be incorrect if other initialized chars follow. + if ( opcode == OP_BLOCK_COPY ) { + expression = ( expression + 3 ) & ~3; + } + + EmitByte( &segment[CODESEG], opcode ); + EmitInt( &segment[CODESEG], expression ); + } else { + EmitByte( &segment[CODESEG], opcode ); + } + + instructionCount++; + return; + } + } + +/* This falls through if an assembly opcode is not found. -PH */ + +/* The following should be sorted in sequence of statistical frequency, most frequent first. -PH */ +/* +Empirical frequency statistics from FI 2001.01.23: + 109892 STAT ADDRL + 72188 STAT BYTE + 51150 STAT LINE + 50906 STAT ARG + 43704 STAT IMPORT + 34902 STAT LABEL + 32066 STAT ADDRF + 23704 STAT CALL + 7720 STAT POP + 7256 STAT RET + 5198 STAT ALIGN + 3292 STAT EXPORT + 2878 STAT PROC + 2878 STAT ENDPROC + 2812 STAT ADDRESS + 738 STAT SKIP + 374 STAT EQU + 280 STAT CODE + 176 STAT LIT + 102 STAT FILE + 100 STAT BSS + 68 STAT DATA + + -PH +*/ + +#undef ASM +#define ASM(O) if (TryAssemble##O ()) return; + + ASM(ADDRL) + ASM(BYTE) + ASM(LINE) + ASM(ARG) + ASM(IMPORT) + ASM(LABEL) + ASM(ADDRF) + ASM(CALL) + ASM(POP) + ASM(RET) + ASM(ALIGN) + ASM(EXPORT) + ASM(PROC) + ASM(ENDPROC) + ASM(ADDRESS) + ASM(SKIP) + ASM(EQU) + ASM(CODE) + ASM(LIT) + ASM(FILE) + ASM(BSS) + ASM(DATA) + + CodeError( "Unknown token: %s\n", token ); +} + +/* +============== +InitTables +============== +*/ +void InitTables( void ) { + int i; + + symtable = hashtable_new(symtablelen); + optable = hashtable_new(100); /* There's hardly 100 opcodes anyway. */ + + for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) { + opcodesHash[i] = HashString( sourceOps[i].name ); + hashtable_add(optable, opcodesHash[i], sourceOps + i); + } +} + + +/* +============== +WriteMapFile +============== +*/ +static void WriteMapFile( void ) { + FILE *f; + symbol_t *s; + char imageName[MAX_OS_PATH]; + int seg; + + strcpy( imageName, outputFilename ); + StripExtension( imageName ); + strcat( imageName, ".map" ); + + report( "Writing %s...\n", imageName ); + + f = SafeOpenWrite( imageName ); + for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) { + for ( s = symbols ; s ; s = s->next ) { + if ( s->name[0] == '$' ) { + continue; // skip locals + } + if ( &segment[seg] != s->segment ) { + continue; + } + fprintf( f, "%i %8x %s\n", seg, s->value, s->name ); + } + } + fclose( f ); +} + +/* +=============== +WriteVmFile +=============== +*/ +static void WriteVmFile( void ) { + char imageName[MAX_OS_PATH]; + vmHeader_t header; + FILE *f; + int headerSize; + + report( "%i total errors\n", errorCount ); + + strcpy( imageName, outputFilename ); + StripExtension( imageName ); + strcat( imageName, ".qvm" ); + + remove( imageName ); + + report( "code segment: %7i\n", segment[CODESEG].imageUsed ); + report( "data segment: %7i\n", segment[DATASEG].imageUsed ); + report( "lit segment: %7i\n", segment[LITSEG].imageUsed ); + report( "bss segment: %7i\n", segment[BSSSEG].imageUsed ); + report( "instruction count: %i\n", instructionCount ); + + if ( errorCount != 0 ) { + report( "Not writing a file due to errors\n" ); + return; + } + + if( !options.vanillaQ3Compatibility ) { + header.vmMagic = VM_MAGIC_VER2; + headerSize = sizeof( header ); + } else { + header.vmMagic = VM_MAGIC; + + // Don't write the VM_MAGIC_VER2 bits when maintaining 1.32b compatibility. + // (I know this isn't strictly correct due to padding, but then platforms + // that pad wouldn't be able to write a correct header anyway). Note: if + // vmHeader_t changes, this needs to be adjusted too. + headerSize = sizeof( header ) - sizeof( header.jtrgLength ); + } + + header.instructionCount = instructionCount; + header.codeOffset = headerSize; + header.codeLength = segment[CODESEG].imageUsed; + header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed; + header.dataLength = segment[DATASEG].imageUsed; + header.litLength = segment[LITSEG].imageUsed; + header.bssLength = segment[BSSSEG].imageUsed; + header.jtrgLength = segment[JTRGSEG].imageUsed; + + report( "Writing to %s\n", imageName ); + +#ifdef Q3_BIG_ENDIAN + { + int i; + + // byte swap the header + for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) { + ((int *)&header)[i] = LittleLong( ((int *)&header)[i] ); + } + } +#endif + + CreatePath( imageName ); + f = SafeOpenWrite( imageName ); + SafeWrite( f, &header, headerSize ); + SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed ); + SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed ); + SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed ); + + if( !options.vanillaQ3Compatibility ) { + SafeWrite( f, &segment[JTRGSEG].image, segment[JTRGSEG].imageUsed ); + } + + fclose( f ); +} + +/* +=============== +Assemble +=============== +*/ +static void Assemble( void ) { + int i; + char filename[MAX_OS_PATH]; + char *ptr; + + report( "outputFilename: %s\n", outputFilename ); + + for ( i = 0 ; i < numAsmFiles ; i++ ) { + strcpy( filename, asmFileNames[ i ] ); + DefaultExtension( filename, ".asm" ); + LoadFile( filename, (void **)&asmFiles[i] ); + } + + // assemble + for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) { + segment[LITSEG].segmentBase = segment[DATASEG].imageUsed; + segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed; + segment[JTRGSEG].segmentBase = segment[BSSSEG].segmentBase + segment[BSSSEG].imageUsed; + for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { + segment[i].imageUsed = 0; + } + segment[DATASEG].imageUsed = 4; // skip the 0 byte, so NULL pointers are fixed up properly + instructionCount = 0; + + for ( i = 0 ; i < numAsmFiles ; i++ ) { + currentFileIndex = i; + currentFileName = asmFileNames[ i ]; + currentFileLine = 0; + report("pass %i: %s\n", passNumber, currentFileName ); + fflush( NULL ); + ptr = asmFiles[i]; + while ( ptr ) { + ptr = ExtractLine( ptr ); + AssembleLine(); + } + } + + // align all segment + for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { + segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3; + } + if (passNumber == 0) { + sort_symbols(); + } + } + + // reserve the stack in bss + DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed ); + segment[BSSSEG].imageUsed += stackSize; + DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed ); + + // write the image + WriteVmFile(); + + // write the map file even if there were errors + if( options.writeMapFile ) { + WriteMapFile(); + } +} + + +/* +============= +ParseOptionFile + +============= +*/ +static void ParseOptionFile( const char *filename ) { + char expanded[MAX_OS_PATH]; + char *text, *text_p; + + strcpy( expanded, filename ); + DefaultExtension( expanded, ".q3asm" ); + LoadFile( expanded, (void **)&text ); + if ( !text ) { + return; + } + + text_p = text; + + while( ( text_p = COM_Parse( text_p ) ) != 0 ) { + if ( !strcmp( com_token, "-o" ) ) { + // allow output override in option file + text_p = COM_Parse( text_p ); + if ( text_p ) { + strcpy( outputFilename, com_token ); + } + continue; + } + + asmFileNames[ numAsmFiles ] = copystring( com_token ); + numAsmFiles++; + } +} + +static void ShowHelp( char *argv0 ) { + Error("Usage: %s [OPTION]... [FILES]...\n\ +Assemble LCC bytecode assembly to Q3VM bytecode.\n\ +\n\ + -o OUTPUT Write assembled output to file OUTPUT.qvm\n\ + -f LISTFILE Read options and list of files to assemble from LISTFILE.q3asm\n\ + -b BUCKETS Set symbol hash table to BUCKETS buckets\n\ + -m Generate a mapfile for each OUTPUT.qvm\n\ + -v Verbose compilation report\n\ + -vq3 Produce a qvm file compatible with Q3 1.32b\n\ + -h --help -? Show this help\n\ +", argv0); +} + +/* +============== +main +============== +*/ +int main( int argc, char **argv ) { + int i; + double start, end; + +// _chdir( "/quake3/jccode/cgame/lccout" ); // hack for vc profiler + + if ( argc < 2 ) { + ShowHelp( argv[0] ); + } + + start = I_FloatTime (); + + // default filename is "q3asm" + strcpy( outputFilename, "q3asm" ); + numAsmFiles = 0; + + for ( i = 1 ; i < argc ; i++ ) { + if ( argv[i][0] != '-' ) { + break; + } + if( !strcmp( argv[ i ], "-h" ) || + !strcmp( argv[ i ], "--help" ) || + !strcmp( argv[ i ], "-?") ) { + ShowHelp( argv[0] ); + } + + if ( !strcmp( argv[i], "-o" ) ) { + if ( i == argc - 1 ) { + Error( "-o must preceed a filename" ); + } +/* Timbo of Tremulous pointed out -o not working; stock ID q3asm folded in the change. Yay. */ + strcpy( outputFilename, argv[ i+1 ] ); + i++; + continue; + } + + if ( !strcmp( argv[i], "-f" ) ) { + if ( i == argc - 1 ) { + Error( "-f must preceed a filename" ); + } + ParseOptionFile( argv[ i+1 ] ); + i++; + continue; + } + + if (!strcmp(argv[i], "-b")) { + if (i == argc - 1) { + Error("-b requires an argument"); + } + i++; + symtablelen = atoiNoCap(argv[i]); + continue; + } + + if( !strcmp( argv[ i ], "-v" ) ) { +/* Verbosity option added by Timbo, 2002.09.14. +By default (no -v option), q3asm remains silent except for critical errors. +Verbosity turns on all messages, error or not. +Motivation: not wanting to scrollback for pages to find asm error. +*/ + options.verbose = qtrue; + continue; + } + + if( !strcmp( argv[ i ], "-m" ) ) { + options.writeMapFile = qtrue; + continue; + } + + if( !strcmp( argv[ i ], "-vq3" ) ) { + options.vanillaQ3Compatibility = qtrue; + continue; + } + + Error( "Unknown option: %s", argv[i] ); + } + + // the rest of the command line args are asm files + for ( ; i < argc ; i++ ) { + asmFileNames[ numAsmFiles ] = copystring( argv[ i ] ); + numAsmFiles++; + } + // In some case it Segfault without this check + if ( numAsmFiles == 0 ) { + Error( "No file to assemble\n" ); + } + + InitTables(); + Assemble(); + + { + symbol_t *s; + + for ( i = 0, s = symbols ; s ; s = s->next, i++ ) /* nop */ ; + + if (options.verbose) + { + report("%d symbols defined\n", i); + hashtable_stats(symtable); + hashtable_stats(optable); + } + } + + end = I_FloatTime (); + report ("%5.0f seconds elapsed\n", end-start); + + return errorCount; +} + diff --git a/src/tools/lcc/COPYRIGHT b/src/tools/lcc/COPYRIGHT new file mode 100644 index 0000000..961a48f --- /dev/null +++ b/src/tools/lcc/COPYRIGHT @@ -0,0 +1,61 @@ +The authors of this software are Christopher W. Fraser and +David R. Hanson. + +Copyright (c) 1991,1992,1993,1994,1995,1996,1997,1998 by AT&T, +Christopher W. Fraser, and David R. Hanson. All Rights Reserved. + +Permission to use, copy, modify, and distribute this software for any +purpose, subject to the provisions described below, without fee is +hereby granted, provided that this entire notice is included in all +copies of any software that is or includes a copy or modification of +this software and in all copies of the supporting documentation for +such software. + +THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY +REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + + +lcc is not public-domain software, shareware, and it is not protected +by a `copyleft' agreement, like the code from the Free Software +Foundation. + +lcc is available free for your personal research and instructional use +under the `fair use' provisions of the copyright law. You may, however, +redistribute lcc in whole or in part provided you acknowledge its +source and include this CPYRIGHT file. You may, for example, include +the distribution in a CDROM of free software, provided you charge only +for the media, or mirror the distribution files at your site. + +You may not sell lcc or any product derived from it in which it is a +significant part of the value of the product. Using the lcc front end +to build a C syntax checker is an example of this kind of product. + +You may use parts of lcc in products as long as you charge for only +those components that are entirely your own and you acknowledge the use +of lcc clearly in all product documentation and distribution media. You +must state clearly that your product uses or is based on parts of lcc +and that lcc is available free of charge. You must also request that +bug reports on your product be reported to you. Using the lcc front +end to build a C compiler for the Motorola 88000 chip and charging for +and distributing only the 88000 code generator is an example of this +kind of product. + +Using parts of lcc in other products is more problematic. For example, +using parts of lcc in a C++ compiler could save substantial time and +effort and therefore contribute significantly to the profitability of +the product. This kind of use, or any use where others stand to make a +profit from what is primarily our work, requires a license agreement +with Addison-Wesley. Per-copy and unlimited use licenses are +available; for more information, contact + + J. Carter Shanklin + Addison Wesley Longman, Inc. + 2725 Sand Hill Rd. + Menlo Park, CA 94025 + 650/854-0300 x2478 FAX: 650/614-2930 jcs@awl.com +----- +Chris Fraser / cwfraser@microsoft.com +David Hanson / drh@microsoft.com +$Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ diff --git a/src/tools/lcc/LOG b/src/tools/lcc/LOG new file mode 100644 index 0000000..dd23f62 --- /dev/null +++ b/src/tools/lcc/LOG @@ -0,0 +1,91 @@ +From lcc 4.0 to 4.1: + +Changes: + +See doc/4.html for changes in the code-generation interface. + +Warns about constants that are too large, eg, short x = 70000; + +Warns about expressions that have no effect. + +Unsigned shorts are now used for wide-character constants, and +wchar_t is a typedef for unsigned short. + +More assertions in gen.c to confirm that the register allocator is +configured correctly; ie, that the various masks, wildcards, +clobbers, and targets are internally consistent. Full checking +appears impractical, but there's still more than than there was +before. + +On the SPARC, lcc now emits .type and .size directives +unconditionally. + +On the x86, constants are now emitted into the text segment. + +If the environment variable "LCCDIR" is defined, it gives the directory +that contains the preprocessor, the compiler proper, and the +lcc-specific libraries. + +Under Windows, lcc searches the directories named in the environment +variable "include" for header files. + +Errors fixed: + +Erroneously complained about unknown sizes for some const fields, eg, +typedef struct foo ref; struct foo { const ref *q; int a; }; +f(ref *p, int i) { return p->q[i].a; } + +-A -A erroneously complained about static main's that didn't conform +to the ANSI-mandated "int main(void)" or "int main(int, char **)". + +Silently generated incorrect code for a structure copy with a +post-incremented target, eg, +struct { int x; } data = {1}, copy[2], *q = copy; +main() { *q++ = data; } + +Generated incorrect values in some expressions with constant pointers. + +Silently truncated string literals longer than 4095 characters. + +Failed to emit debugging information for uninitialized globals. + +Failed to diagnose missing sizes in some multi-dimensioned array +declarators, eg, extern int x[][10]; int x[5][]; + +Silently emitted incorrect sizes and initalizations for some +incomplete multi-dimensioned arrays involving pointers and whose size +is determined by the number of initializers. + +Set only the x.name field for some back-end symbols (eg, wildcards), +and the uninitialized name field crashed some debugging output. + +uses() failed to check the register *set* as well as the register +mask. There's no known bug demo, but a wildcard set might be +contrived that would need the test. + +Crashed with -b on some conditional expressions involving calls, eg, +int p; void g(void) { p ? f() : 1; } + +On the MIPS, sometimes generated an incorrect frame size and thus a +crash when floating-point registers were saved. + +On the SPARC, erroneously reused a register variable as a temporary +when the variable is compiler-generated. + +On the SPARC with -b, emitted incorrect code for returning structs. + +On the x86, conversion from float to int rounded instead of truncated +with the default floating-point mode. + +On the x86, eliminate rtargets for kids after the first (see p. 419). + +On the x86, substitute reg for freg, in order to use the common reg +rules. Needed only for debugging output, since we're not using any +float regs as regs at this time. + +On the x86, "double f(); main(){f();}" wasn't popping the FP register stack. + +On the x86, ECX was saved by the callee, when it should have been +saved by the caller. + +$Id: LOG 145 2001-10-17 21:53:10Z timo $ diff --git a/src/tools/lcc/README b/src/tools/lcc/README new file mode 100644 index 0000000..4ba4d3f --- /dev/null +++ b/src/tools/lcc/README @@ -0,0 +1,21 @@ +This hierarchy is the distribution for lcc version 4.1. + +lcc version 3.x is described in the book "A Retargetable C Compiler: +Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1). +There are significant differences between 3.x and 4.x, most notably in +the intermediate code. doc/4.html summarizes the differences. + +VERSION 4.1 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT +UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION. + +LOG describes the changes since the last release. + +CPYRIGHT describes the conditions under you can use, copy, modify, and +distribute lcc or works derived from lcc. + +doc/install.html is an HTML file that gives a complete description of +the distribution and installation instructions. + +Chris Fraser / cwfraser@microsoft.com +David Hanson / drh@microsoft.com +$Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ diff --git a/src/tools/lcc/README.id b/src/tools/lcc/README.id new file mode 100644 index 0000000..6611a37 --- /dev/null +++ b/src/tools/lcc/README.id @@ -0,0 +1,3 @@ +2001-10-31 Timothee Besset <ttimo@idsoftware.com> +updated from the $/source/lcc code +modified for portability and use with >= 1.31 mod source release diff --git a/src/tools/lcc/cpp/cpp.c b/src/tools/lcc/cpp/cpp.c new file mode 100644 index 0000000..1fcffbc --- /dev/null +++ b/src/tools/lcc/cpp/cpp.c @@ -0,0 +1,327 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include "cpp.h" + +char rcsid[] = "cpp.c - faked rcsid"; + +#define OUTS 16384 +char outbuf[OUTS]; +char *outp = outbuf; +Source *cursource; +int nerrs; +struct token nltoken = { NL, 0, 0, 0, 1, (uchar*)"\n" }; +char *curtime; +int incdepth; +int ifdepth; +int ifsatisfied[NIF]; +int skipping; + + +int +main(int argc, char **argv) +{ + Tokenrow tr; + time_t t; + char ebuf[BUFSIZ]; + + setbuf(stderr, ebuf); + t = time(NULL); + curtime = ctime(&t); + maketokenrow(3, &tr); + expandlex(); + setup(argc, argv); + fixlex(); + iniths(); + genline(); + process(&tr); + flushout(); + fflush(stderr); + exit(nerrs > 0); + return 0; +} + +void +process(Tokenrow *trp) +{ + int anymacros = 0; + + for (;;) { + if (trp->tp >= trp->lp) { + trp->tp = trp->lp = trp->bp; + outp = outbuf; + anymacros |= gettokens(trp, 1); + trp->tp = trp->bp; + } + if (trp->tp->type == END) { + if (--incdepth>=0) { + if (cursource->ifdepth) + error(ERROR, + "Unterminated conditional in #include"); + unsetsource(); + cursource->line += cursource->lineinc; + trp->tp = trp->lp; + genline(); + continue; + } + if (ifdepth) + error(ERROR, "Unterminated #if/#ifdef/#ifndef"); + break; + } + if (trp->tp->type==SHARP) { + trp->tp += 1; + control(trp); + } else if (!skipping && anymacros) + expandrow(trp, NULL); + if (skipping) + setempty(trp); + puttokens(trp); + anymacros = 0; + cursource->line += cursource->lineinc; + if (cursource->lineinc>1) { + genline(); + } + } +} + +void +control(Tokenrow *trp) +{ + Nlist *np; + Token *tp; + + tp = trp->tp; + if (tp->type!=NAME) { + if (tp->type==NUMBER) + goto kline; + if (tp->type != NL) + error(ERROR, "Unidentifiable control line"); + return; /* else empty line */ + } + if ((np = lookup(tp, 0))==NULL || ((np->flag&ISKW)==0 && !skipping)) { + error(WARNING, "Unknown preprocessor control %t", tp); + return; + } + if (skipping) { + switch (np->val) { + case KENDIF: + if (--ifdepth<skipping) + skipping = 0; + --cursource->ifdepth; + setempty(trp); + return; + + case KIFDEF: + case KIFNDEF: + case KIF: + if (++ifdepth >= NIF) + error(FATAL, "#if too deeply nested"); + ++cursource->ifdepth; + return; + + case KELIF: + case KELSE: + if (ifdepth<=skipping) + break; + return; + + default: + return; + } + } + switch (np->val) { + case KDEFINE: + dodefine(trp); + break; + + case KUNDEF: + tp += 1; + if (tp->type!=NAME || trp->lp - trp->bp != 4) { + error(ERROR, "Syntax error in #undef"); + break; + } + if ((np = lookup(tp, 0)) != NULL) + np->flag &= ~ISDEFINED; + break; + + case KPRAGMA: + return; + + case KIFDEF: + case KIFNDEF: + case KIF: + if (++ifdepth >= NIF) + error(FATAL, "#if too deeply nested"); + ++cursource->ifdepth; + ifsatisfied[ifdepth] = 0; + if (eval(trp, np->val)) + ifsatisfied[ifdepth] = 1; + else + skipping = ifdepth; + break; + + case KELIF: + if (ifdepth==0) { + error(ERROR, "#elif with no #if"); + return; + } + if (ifsatisfied[ifdepth]==2) + error(ERROR, "#elif after #else"); + if (eval(trp, np->val)) { + if (ifsatisfied[ifdepth]) + skipping = ifdepth; + else { + skipping = 0; + ifsatisfied[ifdepth] = 1; + } + } else + skipping = ifdepth; + break; + + case KELSE: + if (ifdepth==0 || cursource->ifdepth==0) { + error(ERROR, "#else with no #if"); + return; + } + if (ifsatisfied[ifdepth]==2) + error(ERROR, "#else after #else"); + if (trp->lp - trp->bp != 3) + error(ERROR, "Syntax error in #else"); + skipping = ifsatisfied[ifdepth]? ifdepth: 0; + ifsatisfied[ifdepth] = 2; + break; + + case KENDIF: + if (ifdepth==0 || cursource->ifdepth==0) { + error(ERROR, "#endif with no #if"); + return; + } + --ifdepth; + --cursource->ifdepth; + if (trp->lp - trp->bp != 3) + error(WARNING, "Syntax error in #endif"); + break; + + case KWARNING: + trp->tp = tp+1; + error(WARNING, "#warning directive: %r", trp); + break; + + case KERROR: + trp->tp = tp+1; + error(ERROR, "#error directive: %r", trp); + break; + + case KLINE: + trp->tp = tp+1; + expandrow(trp, "<line>"); + tp = trp->bp+2; + kline: + if (tp+1>=trp->lp || tp->type!=NUMBER || tp+3<trp->lp + || ((tp+3==trp->lp && ((tp+1)->type!=STRING))||*(tp+1)->t=='L')){ + error(ERROR, "Syntax error in #line"); + return; + } + cursource->line = atol((char*)tp->t)-1; + if (cursource->line<0 || cursource->line>=32768) + error(WARNING, "#line specifies number out of range"); + tp = tp+1; + if (tp+1<trp->lp) + cursource->filename=(char*)newstring(tp->t+1,tp->len-2,0); + return; + + case KDEFINED: + error(ERROR, "Bad syntax for control line"); + break; + + case KINCLUDE: + doinclude(trp); + trp->lp = trp->bp; + return; + + case KEVAL: + eval(trp, np->val); + break; + + default: + error(ERROR, "Preprocessor control `%t' not yet implemented", tp); + break; + } + setempty(trp); + return; +} + +void * +domalloc(int size) +{ + void *p = malloc(size); + + if (p==NULL) + error(FATAL, "Out of memory from malloc"); + return p; +} + +void +dofree(void *p) +{ + free(p); +} + +void +error(enum errtype type, char *string, ...) +{ + va_list ap; + char *cp, *ep; + Token *tp; + Tokenrow *trp; + Source *s; + int i; + + fprintf(stderr, "cpp: "); + for (s=cursource; s; s=s->next) + if (*s->filename) + fprintf(stderr, "%s:%d ", s->filename, s->line); + va_start(ap, string); + for (ep=string; *ep; ep++) { + if (*ep=='%') { + switch (*++ep) { + + case 's': + cp = va_arg(ap, char *); + fprintf(stderr, "%s", cp); + break; + case 'd': + i = va_arg(ap, int); + fprintf(stderr, "%d", i); + break; + case 't': + tp = va_arg(ap, Token *); + fprintf(stderr, "%.*s", tp->len, tp->t); + break; + + case 'r': + trp = va_arg(ap, Tokenrow *); + for (tp=trp->tp; tp<trp->lp&&tp->type!=NL; tp++) { + if (tp>trp->tp && tp->wslen) + fputc(' ', stderr); + fprintf(stderr, "%.*s", tp->len, tp->t); + } + break; + + default: + fputc(*ep, stderr); + break; + } + } else + fputc(*ep, stderr); + } + va_end(ap); + fputc('\n', stderr); + if (type==FATAL) + exit(1); + if (type!=WARNING) + nerrs = 1; + fflush(stderr); +} diff --git a/src/tools/lcc/cpp/cpp.h b/src/tools/lcc/cpp/cpp.h new file mode 100644 index 0000000..87871d9 --- /dev/null +++ b/src/tools/lcc/cpp/cpp.h @@ -0,0 +1,166 @@ +#define INS 32768 /* input buffer */ +#define OBS 4096 /* outbut buffer */ +#define NARG 32 /* Max number arguments to a macro */ +#define NINCLUDE 32 /* Max number of include directories (-I) */ +#define NIF 32 /* depth of nesting of #if */ +#ifndef EOF +#define EOF (-1) +#endif +#ifndef NULL +#define NULL 0 +#endif + +#ifndef __alpha +typedef unsigned char uchar; +#endif + +enum toktype { END, UNCLASS, NAME, NUMBER, STRING, CCON, NL, WS, DSHARP, + EQ, NEQ, LEQ, GEQ, LSH, RSH, LAND, LOR, PPLUS, MMINUS, + ARROW, SBRA, SKET, LP, RP, DOT, AND, STAR, PLUS, MINUS, + TILDE, NOT, SLASH, PCT, LT, GT, CIRC, OR, QUEST, + COLON, ASGN, COMMA, SHARP, SEMIC, CBRA, CKET, + ASPLUS, ASMINUS, ASSTAR, ASSLASH, ASPCT, ASCIRC, ASLSH, + ASRSH, ASOR, ASAND, ELLIPS, + DSHARP1, NAME1, DEFINED, UMINUS }; + +enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE, KDEFINE, + KUNDEF, KLINE, KWARNING, KERROR, KPRAGMA, KDEFINED, + KLINENO, KFILE, KDATE, KTIME, KSTDC, KEVAL }; + +#define ISDEFINED 01 /* has #defined value */ +#define ISKW 02 /* is PP keyword */ +#define ISUNCHANGE 04 /* can't be #defined in PP */ +#define ISMAC 010 /* builtin macro, e.g. __LINE__ */ + +#define EOB 0xFE /* sentinel for end of input buffer */ +#define EOFC 0xFD /* sentinel for end of input file */ +#define XPWS 1 /* token flag: white space to assure token sep. */ + +typedef struct token { + unsigned char type; + unsigned char flag; + unsigned short hideset; + unsigned int wslen; + unsigned int len; + uchar *t; +} Token; + +typedef struct tokenrow { + Token *tp; /* current one to scan */ + Token *bp; /* base (allocated value) */ + Token *lp; /* last+1 token used */ + int max; /* number allocated */ +} Tokenrow; + +typedef struct source { + char *filename; /* name of file of the source */ + int line; /* current line number */ + int lineinc; /* adjustment for \\n lines */ + uchar *inb; /* input buffer */ + uchar *inp; /* input pointer */ + uchar *inl; /* end of input */ + int fd; /* input source */ + int ifdepth; /* conditional nesting in include */ + struct source *next; /* stack for #include */ +} Source; + +typedef struct nlist { + struct nlist *next; + uchar *name; + int len; + Tokenrow *vp; /* value as macro */ + Tokenrow *ap; /* list of argument names, if any */ + char val; /* value as preprocessor name */ + char flag; /* is defined, is pp name */ +} Nlist; + +typedef struct includelist { + char deleted; + char always; + char *file; +} Includelist; + +#define new(t) (t *)domalloc(sizeof(t)) +#define quicklook(a,b) (namebit[(a)&077] & (1<<((b)&037))) +#define quickset(a,b) namebit[(a)&077] |= (1<<((b)&037)) +extern unsigned long namebit[077+1]; + +enum errtype { WARNING, ERROR, FATAL }; + +void expandlex(void); +void fixlex(void); +void setup(int, char **); +int gettokens(Tokenrow *, int); +int comparetokens(Tokenrow *, Tokenrow *); +Source *setsource(char *, int, char *); +void unsetsource(void); +void puttokens(Tokenrow *); +void process(Tokenrow *); +void *domalloc(int); +void dofree(void *); +void error(enum errtype, char *, ...); +void flushout(void); +int fillbuf(Source *); +int trigraph(Source *); +int foldline(Source *); +Nlist *lookup(Token *, int); +void control(Tokenrow *); +void dodefine(Tokenrow *); +void doadefine(Tokenrow *, int); +void doinclude(Tokenrow *); +void appendDirToIncludeList( char *dir ); +void doif(Tokenrow *, enum kwtype); +void expand(Tokenrow *, Nlist *); +void builtin(Tokenrow *, int); +int gatherargs(Tokenrow *, Tokenrow **, int *); +void substargs(Nlist *, Tokenrow *, Tokenrow **); +void expandrow(Tokenrow *, char *); +void maketokenrow(int, Tokenrow *); +Tokenrow *copytokenrow(Tokenrow *, Tokenrow *); +Token *growtokenrow(Tokenrow *); +Tokenrow *normtokenrow(Tokenrow *); +void adjustrow(Tokenrow *, int); +void movetokenrow(Tokenrow *, Tokenrow *); +void insertrow(Tokenrow *, int, Tokenrow *); +void peektokens(Tokenrow *, char *); +void doconcat(Tokenrow *); +Tokenrow *stringify(Tokenrow *); +int lookuparg(Nlist *, Token *); +long eval(Tokenrow *, int); +void genline(void); +void setempty(Tokenrow *); +void makespace(Tokenrow *); +char *outnum(char *, int); +int digit(int); +uchar *newstring(uchar *, int, int); +int checkhideset(int, Nlist *); +void prhideset(int); +int newhideset(int, Nlist *); +int unionhideset(int, int); +void iniths(void); +void setobjname(char *); +#define rowlen(tokrow) ((tokrow)->lp - (tokrow)->bp) + +char *basepath( char *fname ); + +extern char *outp; +extern Token nltoken; +extern Source *cursource; +extern char *curtime; +extern int incdepth; +extern int ifdepth; +extern int ifsatisfied[NIF]; +extern int Mflag; +extern int skipping; +extern int verbose; +extern int Cplusplus; +extern Nlist *kwdefined; +extern Includelist includelist[NINCLUDE]; +extern char wd[]; + +#ifndef _WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif +#include <fcntl.h> diff --git a/src/tools/lcc/cpp/eval.c b/src/tools/lcc/cpp/eval.c new file mode 100644 index 0000000..95a9e11 --- /dev/null +++ b/src/tools/lcc/cpp/eval.c @@ -0,0 +1,524 @@ +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +#define NSTAK 32 +#define SGN 0 +#define UNS 1 +#define UND 2 + +#define UNSMARK 0x1000 + +struct value { + long val; + int type; +}; + +/* conversion types */ +#define RELAT 1 +#define ARITH 2 +#define LOGIC 3 +#define SPCL 4 +#define SHIFT 5 +#define UNARY 6 + +/* operator priority, arity, and conversion type, indexed by tokentype */ +struct pri { + char pri; + char arity; + char ctype; +} priority[] = { + { 0, 0, 0 }, /* END */ + { 0, 0, 0 }, /* UNCLASS */ + { 0, 0, 0 }, /* NAME */ + { 0, 0, 0 }, /* NUMBER */ + { 0, 0, 0 }, /* STRING */ + { 0, 0, 0 }, /* CCON */ + { 0, 0, 0 }, /* NL */ + { 0, 0, 0 }, /* WS */ + { 0, 0, 0 }, /* DSHARP */ + { 11, 2, RELAT }, /* EQ */ + { 11, 2, RELAT }, /* NEQ */ + { 12, 2, RELAT }, /* LEQ */ + { 12, 2, RELAT }, /* GEQ */ + { 13, 2, SHIFT }, /* LSH */ + { 13, 2, SHIFT }, /* RSH */ + { 7, 2, LOGIC }, /* LAND */ + { 6, 2, LOGIC }, /* LOR */ + { 0, 0, 0 }, /* PPLUS */ + { 0, 0, 0 }, /* MMINUS */ + { 0, 0, 0 }, /* ARROW */ + { 0, 0, 0 }, /* SBRA */ + { 0, 0, 0 }, /* SKET */ + { 3, 0, 0 }, /* LP */ + { 3, 0, 0 }, /* RP */ + { 0, 0, 0 }, /* DOT */ + { 10, 2, ARITH }, /* AND */ + { 15, 2, ARITH }, /* STAR */ + { 14, 2, ARITH }, /* PLUS */ + { 14, 2, ARITH }, /* MINUS */ + { 16, 1, UNARY }, /* TILDE */ + { 16, 1, UNARY }, /* NOT */ + { 15, 2, ARITH }, /* SLASH */ + { 15, 2, ARITH }, /* PCT */ + { 12, 2, RELAT }, /* LT */ + { 12, 2, RELAT }, /* GT */ + { 9, 2, ARITH }, /* CIRC */ + { 8, 2, ARITH }, /* OR */ + { 5, 2, SPCL }, /* QUEST */ + { 5, 2, SPCL }, /* COLON */ + { 0, 0, 0 }, /* ASGN */ + { 4, 2, 0 }, /* COMMA */ + { 0, 0, 0 }, /* SHARP */ + { 0, 0, 0 }, /* SEMIC */ + { 0, 0, 0 }, /* CBRA */ + { 0, 0, 0 }, /* CKET */ + { 0, 0, 0 }, /* ASPLUS */ + { 0, 0, 0 }, /* ASMINUS */ + { 0, 0, 0 }, /* ASSTAR */ + { 0, 0, 0 }, /* ASSLASH */ + { 0, 0, 0 }, /* ASPCT */ + { 0, 0, 0 }, /* ASCIRC */ + { 0, 0, 0 }, /* ASLSH */ + { 0, 0, 0 }, /* ASRSH */ + { 0, 0, 0 }, /* ASOR */ + { 0, 0, 0 }, /* ASAND */ + { 0, 0, 0 }, /* ELLIPS */ + { 0, 0, 0 }, /* DSHARP1 */ + { 0, 0, 0 }, /* NAME1 */ + { 16, 1, UNARY }, /* DEFINED */ + { 16, 0, UNARY }, /* UMINUS */ +}; + +int evalop(struct pri); +struct value tokval(Token *); +struct value vals[NSTAK], *vp; +enum toktype ops[NSTAK], *op; + +/* + * Evaluate an #if #elif #ifdef #ifndef line. trp->tp points to the keyword. + */ +long +eval(Tokenrow *trp, int kw) +{ + Token *tp; + Nlist *np; + int ntok, rand; + + trp->tp++; + if (kw==KIFDEF || kw==KIFNDEF) { + if (trp->lp - trp->bp != 4 || trp->tp->type!=NAME) { + error(ERROR, "Syntax error in #ifdef/#ifndef"); + return 0; + } + np = lookup(trp->tp, 0); + return (kw==KIFDEF) == (np && np->flag&(ISDEFINED|ISMAC)); + } + ntok = trp->tp - trp->bp; + kwdefined->val = KDEFINED; /* activate special meaning of defined */ + expandrow(trp, "<if>"); + kwdefined->val = NAME; + vp = vals; + op = ops; + *op++ = END; + for (rand=0, tp = trp->bp+ntok; tp < trp->lp; tp++) { + switch(tp->type) { + case WS: + case NL: + continue; + + /* nilary */ + case NAME: + case NAME1: + case NUMBER: + case CCON: + case STRING: + if (rand) + goto syntax; + *vp++ = tokval(tp); + rand = 1; + continue; + + /* unary */ + case DEFINED: + case TILDE: + case NOT: + if (rand) + goto syntax; + *op++ = tp->type; + continue; + + /* unary-binary */ + case PLUS: case MINUS: case STAR: case AND: + if (rand==0) { + if (tp->type==MINUS) + *op++ = UMINUS; + if (tp->type==STAR || tp->type==AND) { + error(ERROR, "Illegal operator * or & in #if/#elsif"); + return 0; + } + continue; + } + /* flow through */ + + /* plain binary */ + case EQ: case NEQ: case LEQ: case GEQ: case LSH: case RSH: + case LAND: case LOR: case SLASH: case PCT: + case LT: case GT: case CIRC: case OR: case QUEST: + case COLON: case COMMA: + if (rand==0) + goto syntax; + if (evalop(priority[tp->type])!=0) + return 0; + *op++ = tp->type; + rand = 0; + continue; + + case LP: + if (rand) + goto syntax; + *op++ = LP; + continue; + + case RP: + if (!rand) + goto syntax; + if (evalop(priority[RP])!=0) + return 0; + if (op<=ops || op[-1]!=LP) { + goto syntax; + } + op--; + continue; + + default: + error(ERROR,"Bad operator (%t) in #if/#elsif", tp); + return 0; + } + } + if (rand==0) + goto syntax; + if (evalop(priority[END])!=0) + return 0; + if (op!=&ops[1] || vp!=&vals[1]) { + error(ERROR, "Botch in #if/#elsif"); + return 0; + } + if (vals[0].type==UND) + error(ERROR, "Undefined expression value"); + return vals[0].val; +syntax: + error(ERROR, "Syntax error in #if/#elsif"); + return 0; +} + +int +evalop(struct pri pri) +{ + struct value v1, v2; + long rv1, rv2; + int rtype, oper; + + /* prevent compiler whining. */ + v1.val = v2.val = 0; + v1.type = v2.type = 0; + + rv2=0; + rtype=0; + while (pri.pri < priority[op[-1]].pri) { + oper = *--op; + if (priority[oper].arity==2) { + v2 = *--vp; + rv2 = v2.val; + } + v1 = *--vp; + rv1 = v1.val; +/*lint -e574 -e644 */ + switch (priority[oper].ctype) { + case 0: + default: + error(WARNING, "Syntax error in #if/#endif"); + return 1; + case ARITH: + case RELAT: + if (v1.type==UNS || v2.type==UNS) + rtype = UNS; + else + rtype = SGN; + if (v1.type==UND || v2.type==UND) + rtype = UND; + if (priority[oper].ctype==RELAT && rtype==UNS) { + oper |= UNSMARK; + rtype = SGN; + } + break; + case SHIFT: + if (v1.type==UND || v2.type==UND) + rtype = UND; + else + rtype = v1.type; + if (rtype==UNS) + oper |= UNSMARK; + break; + case UNARY: + rtype = v1.type; + break; + case LOGIC: + case SPCL: + break; + } + switch (oper) { + case EQ: case EQ|UNSMARK: + rv1 = rv1==rv2; break; + case NEQ: case NEQ|UNSMARK: + rv1 = rv1!=rv2; break; + case LEQ: + rv1 = rv1<=rv2; break; + case GEQ: + rv1 = rv1>=rv2; break; + case LT: + rv1 = rv1<rv2; break; + case GT: + rv1 = rv1>rv2; break; + case LEQ|UNSMARK: + rv1 = (unsigned long)rv1<=rv2; break; + case GEQ|UNSMARK: + rv1 = (unsigned long)rv1>=rv2; break; + case LT|UNSMARK: + rv1 = (unsigned long)rv1<rv2; break; + case GT|UNSMARK: + rv1 = (unsigned long)rv1>rv2; break; + case LSH: + rv1 <<= rv2; break; + case LSH|UNSMARK: + rv1 = (unsigned long)rv1<<rv2; break; + case RSH: + rv1 >>= rv2; break; + case RSH|UNSMARK: + rv1 = (unsigned long)rv1>>rv2; break; + case LAND: + rtype = UND; + if (v1.type==UND) + break; + if (rv1!=0) { + if (v2.type==UND) + break; + rv1 = rv2!=0; + } else + rv1 = 0; + rtype = SGN; + break; + case LOR: + rtype = UND; + if (v1.type==UND) + break; + if (rv1==0) { + if (v2.type==UND) + break; + rv1 = rv2!=0; + } else + rv1 = 1; + rtype = SGN; + break; + case AND: + rv1 &= rv2; break; + case STAR: + rv1 *= rv2; break; + case PLUS: + rv1 += rv2; break; + case MINUS: + rv1 -= rv2; break; + case UMINUS: + if (v1.type==UND) + rtype = UND; + rv1 = -rv1; break; + case OR: + rv1 |= rv2; break; + case CIRC: + rv1 ^= rv2; break; + case TILDE: + rv1 = ~rv1; break; + case NOT: + rv1 = !rv1; if (rtype!=UND) rtype = SGN; break; + case SLASH: + if (rv2==0) { + rtype = UND; + break; + } + if (rtype==UNS) + rv1 /= (unsigned long)rv2; + else + rv1 /= rv2; + break; + case PCT: + if (rv2==0) { + rtype = UND; + break; + } + if (rtype==UNS) + rv1 %= (unsigned long)rv2; + else + rv1 %= rv2; + break; + case COLON: + if (op[-1] != QUEST) + error(ERROR, "Bad ?: in #if/endif"); + else { + op--; + if ((--vp)->val==0) + v1 = v2; + rtype = v1.type; + rv1 = v1.val; + } + break; + case DEFINED: + break; + default: + error(ERROR, "Eval botch (unknown operator)"); + return 1; + } +/*lint +e574 +e644 */ + v1.val = rv1; + v1.type = rtype; + *vp++ = v1; + } + return 0; +} + +struct value +tokval(Token *tp) +{ + struct value v; + Nlist *np; + int i, base, c; + unsigned long n; + uchar *p; + + v.type = SGN; + v.val = 0; + switch (tp->type) { + + case NAME: + v.val = 0; + break; + + case NAME1: + if ((np = lookup(tp, 0)) != NULL && np->flag&(ISDEFINED|ISMAC)) + v.val = 1; + break; + + case NUMBER: + n = 0; + base = 10; + p = tp->t; + c = p[tp->len]; + p[tp->len] = '\0'; + if (*p=='0') { + base = 8; + if (p[1]=='x' || p[1]=='X') { + base = 16; + p++; + } + p++; + } + for (;; p++) { + if ((i = digit(*p)) < 0) + break; + if (i>=base) + error(WARNING, + "Bad digit in number %t", tp); + n *= base; + n += i; + } + if (n>=0x80000000 && base!=10) + v.type = UNS; + for (; *p; p++) { + if (*p=='u' || *p=='U') + v.type = UNS; + else if (*p=='l' || *p=='L') + ; + else { + error(ERROR, + "Bad number %t in #if/#elsif", tp); + break; + } + } + v.val = n; + tp->t[tp->len] = c; + break; + + case CCON: + n = 0; + p = tp->t; + if (*p=='L') { + p += 1; + error(WARNING, "Wide char constant value undefined"); + } + p += 1; + if (*p=='\\') { + p += 1; + if ((i = digit(*p))>=0 && i<=7) { + n = i; + p += 1; + if ((i = digit(*p))>=0 && i<=7) { + p += 1; + n <<= 3; + n += i; + if ((i = digit(*p))>=0 && i<=7) { + p += 1; + n <<= 3; + n += i; + } + } + } else if (*p=='x') { + p += 1; + while ((i = digit(*p))>=0 && i<=15) { + p += 1; + n <<= 4; + n += i; + } + } else { + static char cvcon[] + = "b\bf\fn\nr\rt\tv\v''\"\"??\\\\"; + for (i=0; i<sizeof(cvcon); i+=2) { + if (*p == cvcon[i]) { + n = cvcon[i+1]; + break; + } + } + p += 1; + if (i>=sizeof(cvcon)) + error(WARNING, + "Undefined escape in character constant"); + } + } else if (*p=='\'') + error(ERROR, "Empty character constant"); + else + n = *p++; + if (*p!='\'') + error(WARNING, "Multibyte character constant undefined"); + else if (n>127) + error(WARNING, "Character constant taken as not signed"); + v.val = n; + break; + + case STRING: + error(ERROR, "String in #if/#elsif"); + break; + } + return v; +} + +int +digit(int i) +{ + if ('0'<=i && i<='9') + i -= '0'; + else if ('a'<=i && i<='f') + i -= 'a'-10; + else if ('A'<=i && i<='F') + i -= 'A'-10; + else + i = -1; + return i; +} diff --git a/src/tools/lcc/cpp/getopt.c b/src/tools/lcc/cpp/getopt.c new file mode 100644 index 0000000..c4d1af7 --- /dev/null +++ b/src/tools/lcc/cpp/getopt.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <string.h> +#define EPR fprintf(stderr, +#define ERR(str, chr) if(opterr){EPR "%s%c\n", str, chr);} +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +lcc_getopt (int argc, char *const argv[], const char *opts) +{ + static int sp = 1; + int c; + char *cp; + + if (sp == 1) { + if (optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return -1; + else if (strcmp(argv[optind], "--") == 0) { + optind++; + return -1; + } + } + optopt = c = argv[optind][sp]; + if (c == ':' || (cp=strchr(opts, c)) == 0) { + ERR (": illegal option -- ", c); + if (argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return '?'; + } + if (*++cp == ':') { + if (argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if (++optind >= argc) { + ERR (": option requires an argument -- ", c); + sp = 1; + return '?'; + } else + optarg = argv[optind++]; + sp = 1; + } else { + if (argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = 0; + } + return c; +} diff --git a/src/tools/lcc/cpp/hideset.c b/src/tools/lcc/cpp/hideset.c new file mode 100644 index 0000000..bd2540d --- /dev/null +++ b/src/tools/lcc/cpp/hideset.c @@ -0,0 +1,112 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +/* + * A hideset is a null-terminated array of Nlist pointers. + * They are referred to by indices in the hidesets array. + * Hideset 0 is empty. + */ + +#define HSSIZ 32 +typedef Nlist **Hideset; +Hideset *hidesets; +int nhidesets = 0; +int maxhidesets = 3; +int inserths(Hideset, Hideset, Nlist *); + +/* + * Test for membership in a hideset + */ +int +checkhideset(int hs, Nlist *np) +{ + Hideset hsp; + + if (hs>=nhidesets) + abort(); + for (hsp = hidesets[hs]; *hsp; hsp++) { + if (*hsp == np) + return 1; + } + return 0; +} + +/* + * Return the (possibly new) hideset obtained by adding np to hs. + */ +int +newhideset(int hs, Nlist *np) +{ + int i, len; + Nlist *nhs[HSSIZ+3]; + Hideset hs1, hs2; + + len = inserths(nhs, hidesets[hs], np); + for (i=0; i<nhidesets; i++) { + for (hs1=nhs, hs2=hidesets[i]; *hs1==*hs2; hs1++, hs2++) + if (*hs1 == NULL) + return i; + } + if (len>=HSSIZ) + return hs; + if (nhidesets >= maxhidesets) { + maxhidesets = 3*maxhidesets/2+1; + hidesets = (Hideset *)realloc(hidesets, (sizeof (Hideset *))*maxhidesets); + if (hidesets == NULL) + error(FATAL, "Out of memory from realloc"); + } + hs1 = (Hideset)domalloc(len*sizeof(Hideset)); + memmove(hs1, nhs, len*sizeof(Hideset)); + hidesets[nhidesets] = hs1; + return nhidesets++; +} + +int +inserths(Hideset dhs, Hideset shs, Nlist *np) +{ + Hideset odhs = dhs; + + while (*shs && *shs < np) + *dhs++ = *shs++; + if (*shs != np) + *dhs++ = np; + do { + *dhs++ = *shs; + } while (*shs++); + return dhs - odhs; +} + +/* + * Hideset union + */ +int +unionhideset(int hs1, int hs2) +{ + Hideset hp; + + for (hp = hidesets[hs2]; *hp; hp++) + hs1 = newhideset(hs1, *hp); + return hs1; +} + +void +iniths(void) +{ + hidesets = (Hideset *)domalloc(maxhidesets*sizeof(Hideset *)); + hidesets[0] = (Hideset)domalloc(sizeof(Hideset)); + *hidesets[0] = NULL; + nhidesets++; +} + +void +prhideset(int hs) +{ + Hideset np; + + for (np = hidesets[hs]; *np; np++) { + fprintf(stderr, (char*)(*np)->name, (*np)->len); + fprintf(stderr, " "); + } +} diff --git a/src/tools/lcc/cpp/include.c b/src/tools/lcc/cpp/include.c new file mode 100644 index 0000000..331a0b4 --- /dev/null +++ b/src/tools/lcc/cpp/include.c @@ -0,0 +1,154 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +Includelist includelist[NINCLUDE]; + +extern char *objname; + +void appendDirToIncludeList( char *dir ) +{ + int i; + char *fqdir; + + fqdir = (char *)newstring( (uchar *)includelist[NINCLUDE-1].file, 256, 0 ); + strcat( fqdir, "/" ); + strcat( fqdir, dir ); + + //avoid adding it more than once + for (i=NINCLUDE-2; i>=0; i--) { + if (includelist[i].file && + !strcmp (includelist[i].file, fqdir)) { + return; + } + } + + for (i=NINCLUDE-2; i>=0; i--) { + if (includelist[i].file==NULL) { + includelist[i].always = 1; + includelist[i].file = fqdir; + break; + } + } + if (i<0) + error(FATAL, "Too many -I directives"); +} + +void +doinclude(Tokenrow *trp) +{ + char fname[256], iname[256]; + Includelist *ip; + int angled, len, wlen, fd, i; + + trp->tp += 1; + if (trp->tp>=trp->lp) + goto syntax; + if (trp->tp->type!=STRING && trp->tp->type!=LT) { + len = trp->tp - trp->bp; + expandrow(trp, "<include>"); + trp->tp = trp->bp+len; + } + if (trp->tp->type==STRING) { + len = trp->tp->len-2; + if (len > sizeof(fname) - 1) + len = sizeof(fname) - 1; + strncpy(fname, (char*)trp->tp->t+1, len); + angled = 0; + } else if (trp->tp->type==LT) { + len = 0; + trp->tp++; + while (trp->tp->type!=GT) { + if (trp->tp>trp->lp || len+trp->tp->len+2 >= sizeof(fname)) + goto syntax; + strncpy(fname+len, (char*)trp->tp->t, trp->tp->len); + len += trp->tp->len; + trp->tp++; + } + angled = 1; + } else + goto syntax; + trp->tp += 2; + if (trp->tp < trp->lp || len==0) + goto syntax; + fname[len] = '\0'; + + appendDirToIncludeList( basepath( fname ) ); + + if (fname[0]=='/') { + fd = open(fname, 0); + strcpy(iname, fname); + } else for (fd = -1,i=NINCLUDE-1; i>=0; i--) { + ip = &includelist[i]; + if (ip->file==NULL || ip->deleted || (angled && ip->always==0)) + continue; + if (strlen(fname)+strlen(ip->file)+2 > sizeof(iname)) + continue; + strcpy(iname, ip->file); + strcat(iname, "/"); + strcat(iname, fname); + if ((fd = open(iname, 0)) >= 0) + break; + } + if ( Mflag>1 || (!angled&&Mflag==1) ) { + wlen = write(1,objname,strlen(objname)); + wlen = write(1,iname,strlen(iname)); + wlen = write(1,"\n",1); + } + if (fd >= 0) { + if (++incdepth > 10) + error(FATAL, "#include too deeply nested"); + setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd, NULL); + genline(); + } else { + trp->tp = trp->bp+2; + error(ERROR, "Could not find include file %r", trp); + } + return; +syntax: + error(ERROR, "Syntax error in #include"); + return; +} + +/* + * Generate a line directive for cursource + */ +void +genline(void) +{ + static Token ta = { UNCLASS }; + static Tokenrow tr = { &ta, &ta, &ta+1, 1 }; + uchar *p; + + ta.t = p = (uchar*)outp; + strcpy((char*)p, "#line "); + p += sizeof("#line ")-1; + p = (uchar*)outnum((char*)p, cursource->line); + *p++ = ' '; *p++ = '"'; + if (cursource->filename[0]!='/' && wd[0]) { + strcpy((char*)p, wd); + p += strlen(wd); + *p++ = '/'; + } + strcpy((char*)p, cursource->filename); + p += strlen((char*)p); + *p++ = '"'; *p++ = '\n'; + ta.len = (char*)p-outp; + outp = (char*)p; + tr.tp = tr.bp; + puttokens(&tr); +} + +void +setobjname(char *f) +{ + int n = strlen(f); + objname = (char*)domalloc(n+5); + strcpy(objname,f); + if(objname[n-2]=='.'){ + strcpy(objname+n-1,"$O: "); + }else{ + strcpy(objname+n,"$O: "); + } +} diff --git a/src/tools/lcc/cpp/lex.c b/src/tools/lcc/cpp/lex.c new file mode 100644 index 0000000..8030354 --- /dev/null +++ b/src/tools/lcc/cpp/lex.c @@ -0,0 +1,580 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +/* + * lexical FSM encoding + * when in state state, and one of the characters + * in ch arrives, enter nextstate. + * States >= S_SELF are either final, or at least require special action. + * In 'fsm' there is a line for each state X charset X nextstate. + * List chars that overwrite previous entries later (e.g. C_ALPH + * can be overridden by '_' by a later entry; and C_XX is the + * the universal set, and should always be first. + * States above S_SELF are represented in the big table as negative values. + * S_SELF and S_SELFB encode the resulting token type in the upper bits. + * These actions differ in that S_SELF doesn't have a lookahead char, + * S_SELFB does. + * + * The encoding is blown out into a big table for time-efficiency. + * Entries have + * nextstate: 6 bits; ?\ marker: 1 bit; tokentype: 9 bits. + */ + +#define MAXSTATE 32 +#define ACT(tok,act) ((tok<<7)+act) +#define QBSBIT 0100 +#define GETACT(st) (st>>7)&0x1ff + +/* character classes */ +#define C_WS 1 +#define C_ALPH 2 +#define C_NUM 3 +#define C_EOF 4 +#define C_XX 5 + +enum state { + START=0, NUM1, NUM2, NUM3, ID1, ST1, ST2, ST3, COM1, COM2, COM3, COM4, + CC1, CC2, WS1, PLUS1, MINUS1, STAR1, SLASH1, PCT1, SHARP1, + CIRC1, GT1, GT2, LT1, LT2, OR1, AND1, ASG1, NOT1, DOTS1, + S_SELF=MAXSTATE, S_SELFB, S_EOF, S_NL, S_EOFSTR, + S_STNL, S_COMNL, S_EOFCOM, S_COMMENT, S_EOB, S_WS, S_NAME +}; + +int tottok; +int tokkind[256]; +struct fsm { + int state; /* if in this state */ + uchar ch[4]; /* and see one of these characters */ + int nextstate; /* enter this state if +ve */ +}; + +/*const*/ struct fsm fsm[] = { + /* start state */ + {START, { C_XX }, ACT(UNCLASS,S_SELF)}, + {START, { ' ', '\t', '\v' }, WS1}, + {START, { C_NUM }, NUM1}, + {START, { '.' }, NUM3}, + {START, { C_ALPH }, ID1}, + {START, { 'L' }, ST1}, + {START, { '"' }, ST2}, + {START, { '\'' }, CC1}, + {START, { '/' }, COM1}, + {START, { EOFC }, S_EOF}, + {START, { '\n' }, S_NL}, + {START, { '-' }, MINUS1}, + {START, { '+' }, PLUS1}, + {START, { '<' }, LT1}, + {START, { '>' }, GT1}, + {START, { '=' }, ASG1}, + {START, { '!' }, NOT1}, + {START, { '&' }, AND1}, + {START, { '|' }, OR1}, + {START, { '#' }, SHARP1}, + {START, { '%' }, PCT1}, + {START, { '[' }, ACT(SBRA,S_SELF)}, + {START, { ']' }, ACT(SKET,S_SELF)}, + {START, { '(' }, ACT(LP,S_SELF)}, + {START, { ')' }, ACT(RP,S_SELF)}, + {START, { '*' }, STAR1}, + {START, { ',' }, ACT(COMMA,S_SELF)}, + {START, { '?' }, ACT(QUEST,S_SELF)}, + {START, { ':' }, ACT(COLON,S_SELF)}, + {START, { ';' }, ACT(SEMIC,S_SELF)}, + {START, { '{' }, ACT(CBRA,S_SELF)}, + {START, { '}' }, ACT(CKET,S_SELF)}, + {START, { '~' }, ACT(TILDE,S_SELF)}, + {START, { '^' }, CIRC1}, + + /* saw a digit */ + {NUM1, { C_XX }, ACT(NUMBER,S_SELFB)}, + {NUM1, { C_NUM, C_ALPH, '.' }, NUM1}, + {NUM1, { 'E', 'e' }, NUM2}, + {NUM1, { '_' }, ACT(NUMBER,S_SELFB)}, + + /* saw possible start of exponent, digits-e */ + {NUM2, { C_XX }, ACT(NUMBER,S_SELFB)}, + {NUM2, { '+', '-' }, NUM1}, + {NUM2, { C_NUM, C_ALPH }, NUM1}, + {NUM2, { '_' }, ACT(NUMBER,S_SELFB)}, + + /* saw a '.', which could be a number or an operator */ + {NUM3, { C_XX }, ACT(DOT,S_SELFB)}, + {NUM3, { '.' }, DOTS1}, + {NUM3, { C_NUM }, NUM1}, + + {DOTS1, { C_XX }, ACT(UNCLASS, S_SELFB)}, + {DOTS1, { C_NUM }, NUM1}, + {DOTS1, { '.' }, ACT(ELLIPS, S_SELF)}, + + /* saw a letter or _ */ + {ID1, { C_XX }, ACT(NAME,S_NAME)}, + {ID1, { C_ALPH, C_NUM }, ID1}, + + /* saw L (start of wide string?) */ + {ST1, { C_XX }, ACT(NAME,S_NAME)}, + {ST1, { C_ALPH, C_NUM }, ID1}, + {ST1, { '"' }, ST2}, + {ST1, { '\'' }, CC1}, + + /* saw " beginning string */ + {ST2, { C_XX }, ST2}, + {ST2, { '"' }, ACT(STRING, S_SELF)}, + {ST2, { '\\' }, ST3}, + {ST2, { '\n' }, S_STNL}, + {ST2, { EOFC }, S_EOFSTR}, + + /* saw \ in string */ + {ST3, { C_XX }, ST2}, + {ST3, { '\n' }, S_STNL}, + {ST3, { EOFC }, S_EOFSTR}, + + /* saw ' beginning character const */ + {CC1, { C_XX }, CC1}, + {CC1, { '\'' }, ACT(CCON, S_SELF)}, + {CC1, { '\\' }, CC2}, + {CC1, { '\n' }, S_STNL}, + {CC1, { EOFC }, S_EOFSTR}, + + /* saw \ in ccon */ + {CC2, { C_XX }, CC1}, + {CC2, { '\n' }, S_STNL}, + {CC2, { EOFC }, S_EOFSTR}, + + /* saw /, perhaps start of comment */ + {COM1, { C_XX }, ACT(SLASH, S_SELFB)}, + {COM1, { '=' }, ACT(ASSLASH, S_SELF)}, + {COM1, { '*' }, COM2}, + {COM1, { '/' }, COM4}, + + /* saw / then *, start of comment */ + {COM2, { C_XX }, COM2}, + {COM2, { '\n' }, S_COMNL}, + {COM2, { '*' }, COM3}, + {COM2, { EOFC }, S_EOFCOM}, + + /* saw the * possibly ending a comment */ + {COM3, { C_XX }, COM2}, + {COM3, { '\n' }, S_COMNL}, + {COM3, { '*' }, COM3}, + {COM3, { '/' }, S_COMMENT}, + + /* // comment */ + {COM4, { C_XX }, COM4}, + {COM4, { '\n' }, S_NL}, + {COM4, { EOFC }, S_EOFCOM}, + + /* saw white space, eat it up */ + {WS1, { C_XX }, S_WS}, + {WS1, { ' ', '\t', '\v' }, WS1}, + + /* saw -, check --, -=, -> */ + {MINUS1, { C_XX }, ACT(MINUS, S_SELFB)}, + {MINUS1, { '-' }, ACT(MMINUS, S_SELF)}, + {MINUS1, { '=' }, ACT(ASMINUS,S_SELF)}, + {MINUS1, { '>' }, ACT(ARROW,S_SELF)}, + + /* saw +, check ++, += */ + {PLUS1, { C_XX }, ACT(PLUS, S_SELFB)}, + {PLUS1, { '+' }, ACT(PPLUS, S_SELF)}, + {PLUS1, { '=' }, ACT(ASPLUS, S_SELF)}, + + /* saw <, check <<, <<=, <= */ + {LT1, { C_XX }, ACT(LT, S_SELFB)}, + {LT1, { '<' }, LT2}, + {LT1, { '=' }, ACT(LEQ, S_SELF)}, + {LT2, { C_XX }, ACT(LSH, S_SELFB)}, + {LT2, { '=' }, ACT(ASLSH, S_SELF)}, + + /* saw >, check >>, >>=, >= */ + {GT1, { C_XX }, ACT(GT, S_SELFB)}, + {GT1, { '>' }, GT2}, + {GT1, { '=' }, ACT(GEQ, S_SELF)}, + {GT2, { C_XX }, ACT(RSH, S_SELFB)}, + {GT2, { '=' }, ACT(ASRSH, S_SELF)}, + + /* = */ + {ASG1, { C_XX }, ACT(ASGN, S_SELFB)}, + {ASG1, { '=' }, ACT(EQ, S_SELF)}, + + /* ! */ + {NOT1, { C_XX }, ACT(NOT, S_SELFB)}, + {NOT1, { '=' }, ACT(NEQ, S_SELF)}, + + /* & */ + {AND1, { C_XX }, ACT(AND, S_SELFB)}, + {AND1, { '&' }, ACT(LAND, S_SELF)}, + {AND1, { '=' }, ACT(ASAND, S_SELF)}, + + /* | */ + {OR1, { C_XX }, ACT(OR, S_SELFB)}, + {OR1, { '|' }, ACT(LOR, S_SELF)}, + {OR1, { '=' }, ACT(ASOR, S_SELF)}, + + /* # */ + {SHARP1, { C_XX }, ACT(SHARP, S_SELFB)}, + {SHARP1, { '#' }, ACT(DSHARP, S_SELF)}, + + /* % */ + {PCT1, { C_XX }, ACT(PCT, S_SELFB)}, + {PCT1, { '=' }, ACT(ASPCT, S_SELF)}, + + /* * */ + {STAR1, { C_XX }, ACT(STAR, S_SELFB)}, + {STAR1, { '=' }, ACT(ASSTAR, S_SELF)}, + + /* ^ */ + {CIRC1, { C_XX }, ACT(CIRC, S_SELFB)}, + {CIRC1, { '=' }, ACT(ASCIRC, S_SELF)}, + + {-1} +}; + +/* first index is char, second is state */ +/* increase #states to power of 2 to encourage use of shift */ +short bigfsm[256][MAXSTATE]; + +void +expandlex(void) +{ + /*const*/ struct fsm *fp; + int i, j, nstate; + + for (fp = fsm; fp->state>=0; fp++) { + for (i=0; fp->ch[i]; i++) { + nstate = fp->nextstate; + if (nstate >= S_SELF) + nstate = ~nstate; + switch (fp->ch[i]) { + + case C_XX: /* random characters */ + for (j=0; j<256; j++) + bigfsm[j][fp->state] = nstate; + continue; + case C_ALPH: + for (j=0; j<=256; j++) + if (('a'<=j&&j<='z') || ('A'<=j&&j<='Z') + || j=='_') + bigfsm[j][fp->state] = nstate; + continue; + case C_NUM: + for (j='0'; j<='9'; j++) + bigfsm[j][fp->state] = nstate; + continue; + default: + bigfsm[fp->ch[i]][fp->state] = nstate; + } + } + } + /* install special cases for ? (trigraphs), \ (splicing), runes, and EOB */ + for (i=0; i<MAXSTATE; i++) { + for (j=0; j<0xFF; j++) + if (j=='?' || j=='\\') { + if (bigfsm[j][i]>0) + bigfsm[j][i] = ~bigfsm[j][i]; + bigfsm[j][i] &= ~QBSBIT; + } + bigfsm[EOB][i] = ~S_EOB; + if (bigfsm[EOFC][i]>=0) + bigfsm[EOFC][i] = ~S_EOF; + } +} + +void +fixlex(void) +{ + /* do C++ comments? */ + if (Cplusplus==0) + bigfsm['/'][COM1] = bigfsm['x'][COM1]; +} + +/* + * fill in a row of tokens from input, terminated by NL or END + * First token is put at trp->lp. + * Reset is non-zero when the input buffer can be "rewound." + * The value is a flag indicating that possible macros have + * been seen in the row. + */ +int +gettokens(Tokenrow *trp, int reset) +{ + register int c, state, oldstate; + register uchar *ip; + register Token *tp, *maxp; + int runelen; + Source *s = cursource; + int nmac = 0; + + tp = trp->lp; + ip = s->inp; + if (reset) { + s->lineinc = 0; + if (ip>=s->inl) { /* nothing in buffer */ + s->inl = s->inb; + fillbuf(s); + ip = s->inp = s->inb; + } else if (ip >= s->inb+(3*INS/4)) { + memmove(s->inb, ip, 4+s->inl-ip); + s->inl = s->inb+(s->inl-ip); + ip = s->inp = s->inb; + } + } + maxp = &trp->bp[trp->max]; + runelen = 1; + for (;;) { + continue2: + if (tp>=maxp) { + trp->lp = tp; + tp = growtokenrow(trp); + maxp = &trp->bp[trp->max]; + } + tp->type = UNCLASS; + tp->hideset = 0; + tp->t = ip; + tp->wslen = 0; + tp->flag = 0; + state = START; + for (;;) { + oldstate = state; + c = *ip; + if ((state = bigfsm[c][state]) >= 0) { + ip += runelen; + runelen = 1; + continue; + } + state = ~state; + reswitch: + switch (state&0177) { + case S_SELF: + ip += runelen; + runelen = 1; + case S_SELFB: + tp->type = GETACT(state); + tp->len = ip - tp->t; + tp++; + goto continue2; + + case S_NAME: /* like S_SELFB but with nmac check */ + tp->type = NAME; + tp->len = ip - tp->t; + nmac |= quicklook(tp->t[0], tp->len>1?tp->t[1]:0); + tp++; + goto continue2; + + case S_WS: + tp->wslen = ip - tp->t; + tp->t = ip; + state = START; + continue; + + default: + if ((state&QBSBIT)==0) { + ip += runelen; + runelen = 1; + continue; + } + state &= ~QBSBIT; + s->inp = ip; + if (c=='?') { /* check trigraph */ + if (trigraph(s)) { + state = oldstate; + continue; + } + goto reswitch; + } + if (c=='\\') { /* line-folding */ + if (foldline(s)) { + s->lineinc++; + state = oldstate; + continue; + } + goto reswitch; + } + error(WARNING, "Lexical botch in cpp"); + ip += runelen; + runelen = 1; + continue; + + case S_EOB: + s->inp = ip; + fillbuf(cursource); + state = oldstate; + continue; + + case S_EOF: + tp->type = END; + tp->len = 0; + s->inp = ip; + if (tp!=trp->bp && (tp-1)->type!=NL && cursource->fd!=-1) + error(WARNING,"No newline at end of file"); + trp->lp = tp+1; + return nmac; + + case S_STNL: + error(ERROR, "Unterminated string or char const"); + case S_NL: + tp->t = ip; + tp->type = NL; + tp->len = 1; + tp->wslen = 0; + s->lineinc++; + s->inp = ip+1; + trp->lp = tp+1; + return nmac; + + case S_EOFSTR: + error(FATAL, "EOF in string or char constant"); + break; + + case S_COMNL: + s->lineinc++; + state = COM2; + ip += runelen; + runelen = 1; + if (ip >= s->inb+(7*INS/8)) { /* very long comment */ + memmove(tp->t, ip, 4+s->inl-ip); + s->inl -= ip-tp->t; + ip = tp->t+1; + } + continue; + + case S_EOFCOM: + error(WARNING, "EOF inside comment"); + --ip; + case S_COMMENT: + ++ip; + tp->t = ip; + tp->t[-1] = ' '; + tp->wslen = 1; + state = START; + continue; + } + break; + } + ip += runelen; + runelen = 1; + tp->len = ip - tp->t; + tp++; + } +} + +/* have seen ?; handle the trigraph it starts (if any) else 0 */ +int +trigraph(Source *s) +{ + int c; + + while (s->inp+2 >= s->inl && fillbuf(s)!=EOF) + ; + if (s->inp[1]!='?') + return 0; + c = 0; + switch(s->inp[2]) { + case '=': + c = '#'; break; + case '(': + c = '['; break; + case '/': + c = '\\'; break; + case ')': + c = ']'; break; + case '\'': + c = '^'; break; + case '<': + c = '{'; break; + case '!': + c = '|'; break; + case '>': + c = '}'; break; + case '-': + c = '~'; break; + } + if (c) { + *s->inp = c; + memmove(s->inp+1, s->inp+3, s->inl-s->inp+2); + s->inl -= 2; + } + return c; +} + +int +foldline(Source *s) +{ + while (s->inp+1 >= s->inl && fillbuf(s)!=EOF) + ; + if (s->inp[1] == '\n') { + memmove(s->inp, s->inp+2, s->inl-s->inp+3); + s->inl -= 2; + return 1; + } + return 0; +} + +int +fillbuf(Source *s) +{ + int n, nr; + + nr = INS/8; + if ((char *)s->inl+nr > (char *)s->inb+INS) + error(FATAL, "Input buffer overflow"); + if (s->fd<0 || (n=read(s->fd, (char *)s->inl, INS/8)) <= 0) + n = 0; + if ((*s->inp&0xff) == EOB) /* sentinel character appears in input */ + *s->inp = EOFC; + s->inl += n; + s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOB; + if (n==0) { + s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOFC; + return EOF; + } + return 0; +} + +/* + * Push down to new source of characters. + * If fd>0 and str==NULL, then from a file `name'; + * if fd==-1 and str, then from the string. + */ +Source * +setsource(char *name, int fd, char *str) +{ + Source *s = new(Source); + int len; + + s->line = 1; + s->lineinc = 0; + s->fd = fd; + s->filename = name; + s->next = cursource; + s->ifdepth = 0; + cursource = s; + /* slop at right for EOB */ + if (str) { + len = strlen(str); + s->inb = domalloc(len+4); + s->inp = s->inb; + strncpy((char *)s->inp, str, len); + } else { + s->inb = domalloc(INS+4); + s->inp = s->inb; + len = 0; + } + s->inl = s->inp+len; + s->inl[0] = s->inl[1] = EOB; + return s; +} + +void +unsetsource(void) +{ + Source *s = cursource; + + if (s->fd>=0) { + close(s->fd); + dofree(s->inb); + } + cursource = s->next; + dofree(s); +} diff --git a/src/tools/lcc/cpp/macro.c b/src/tools/lcc/cpp/macro.c new file mode 100644 index 0000000..49d1129 --- /dev/null +++ b/src/tools/lcc/cpp/macro.c @@ -0,0 +1,515 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +/* + * do a macro definition. tp points to the name being defined in the line + */ +void +dodefine(Tokenrow *trp) +{ + Token *tp; + Nlist *np; + Tokenrow *def, *args; + + tp = trp->tp+1; + if (tp>=trp->lp || tp->type!=NAME) { + error(ERROR, "#defined token is not a name"); + return; + } + np = lookup(tp, 1); + if (np->flag&ISUNCHANGE) { + error(ERROR, "#defined token %t can't be redefined", tp); + return; + } + /* collect arguments */ + tp += 1; + args = NULL; + if (tp<trp->lp && tp->type==LP && tp->wslen==0) { + /* macro with args */ + int narg = 0; + tp += 1; + args = new(Tokenrow); + maketokenrow(2, args); + if (tp->type!=RP) { + int err = 0; + for (;;) { + Token *atp; + if (tp->type!=NAME) { + err++; + break; + } + if (narg>=args->max) + growtokenrow(args); + for (atp=args->bp; atp<args->lp; atp++) + if (atp->len==tp->len + && strncmp((char*)atp->t, (char*)tp->t, tp->len)==0) + error(ERROR, "Duplicate macro argument"); + *args->lp++ = *tp; + narg++; + tp += 1; + if (tp->type==RP) + break; + if (tp->type!=COMMA) { + err++; + break; + } + tp += 1; + } + if (err) { + error(ERROR, "Syntax error in macro parameters"); + return; + } + } + tp += 1; + } + trp->tp = tp; + if (((trp->lp)-1)->type==NL) + trp->lp -= 1; + def = normtokenrow(trp); + if (np->flag&ISDEFINED) { + if (comparetokens(def, np->vp) + || (np->ap==NULL) != (args==NULL) + || (np->ap && comparetokens(args, np->ap))) + error(ERROR, "Macro redefinition of %t", trp->bp+2); + } + if (args) { + Tokenrow *tap; + tap = normtokenrow(args); + dofree(args->bp); + args = tap; + } + np->ap = args; + np->vp = def; + np->flag |= ISDEFINED; +} + +/* + * Definition received via -D or -U + */ +void +doadefine(Tokenrow *trp, int type) +{ + Nlist *np; + static Token onetoken[1] = {{ NUMBER, 0, 0, 0, 1, (uchar*)"1" }}; + static Tokenrow onetr = { onetoken, onetoken, onetoken+1, 1 }; + + trp->tp = trp->bp; + if (type=='U') { + if (trp->lp-trp->tp != 2 || trp->tp->type!=NAME) + goto syntax; + if ((np = lookup(trp->tp, 0)) == NULL) + return; + np->flag &= ~ISDEFINED; + return; + } + if (trp->tp >= trp->lp || trp->tp->type!=NAME) + goto syntax; + np = lookup(trp->tp, 1); + np->flag |= ISDEFINED; + trp->tp += 1; + if (trp->tp >= trp->lp || trp->tp->type==END) { + np->vp = &onetr; + return; + } + if (trp->tp->type!=ASGN) + goto syntax; + trp->tp += 1; + if ((trp->lp-1)->type == END) + trp->lp -= 1; + np->vp = normtokenrow(trp); + return; +syntax: + error(FATAL, "Illegal -D or -U argument %r", trp); +} + +/* + * Do macro expansion in a row of tokens. + * Flag is NULL if more input can be gathered. + */ +void +expandrow(Tokenrow *trp, char *flag) +{ + Token *tp; + Nlist *np; + + if (flag) + setsource(flag, -1, ""); + for (tp = trp->tp; tp<trp->lp; ) { + if (tp->type!=NAME + || quicklook(tp->t[0], tp->len>1?tp->t[1]:0)==0 + || (np = lookup(tp, 0))==NULL + || (np->flag&(ISDEFINED|ISMAC))==0 + || (tp->hideset && checkhideset(tp->hideset, np))) { + tp++; + continue; + } + trp->tp = tp; + if (np->val==KDEFINED) { + tp->type = DEFINED; + if ((tp+1)<trp->lp && (tp+1)->type==NAME) + (tp+1)->type = NAME1; + else if ((tp+3)<trp->lp && (tp+1)->type==LP + && (tp+2)->type==NAME && (tp+3)->type==RP) + (tp+2)->type = NAME1; + else + error(ERROR, "Incorrect syntax for `defined'"); + tp++; + continue; + } + if (np->flag&ISMAC) + builtin(trp, np->val); + else { + expand(trp, np); + } + tp = trp->tp; + } + if (flag) + unsetsource(); +} + +/* + * Expand the macro whose name is np, at token trp->tp, in the tokenrow. + * Return trp->tp at the first token next to be expanded + * (ordinarily the beginning of the expansion) + */ +void +expand(Tokenrow *trp, Nlist *np) +{ + Tokenrow ntr; + int ntokc, narg, i; + Token *tp; + Tokenrow *atr[NARG+1]; + int hs; + + copytokenrow(&ntr, np->vp); /* copy macro value */ + if (np->ap==NULL) /* parameterless */ + ntokc = 1; + else { + ntokc = gatherargs(trp, atr, &narg); + if (narg<0) { /* not actually a call (no '(') */ + trp->tp++; + return; + } + if (narg != rowlen(np->ap)) { + error(ERROR, "Disagreement in number of macro arguments"); + trp->tp->hideset = newhideset(trp->tp->hideset, np); + trp->tp += ntokc; + return; + } + substargs(np, &ntr, atr); /* put args into replacement */ + for (i=0; i<narg; i++) { + dofree(atr[i]->bp); + dofree(atr[i]); + } + } + doconcat(&ntr); /* execute ## operators */ + hs = newhideset(trp->tp->hideset, np); + for (tp=ntr.bp; tp<ntr.lp; tp++) { /* distribute hidesets */ + if (tp->type==NAME) { + if (tp->hideset==0) + tp->hideset = hs; + else + tp->hideset = unionhideset(tp->hideset, hs); + } + } + ntr.tp = ntr.bp; + insertrow(trp, ntokc, &ntr); + trp->tp -= rowlen(&ntr); + dofree(ntr.bp); + return; +} + +/* + * Gather an arglist, starting in trp with tp pointing at the macro name. + * Return total number of tokens passed, stash number of args found. + * trp->tp is not changed relative to the tokenrow. + */ +int +gatherargs(Tokenrow *trp, Tokenrow **atr, int *narg) +{ + int parens = 1; + int ntok = 0; + Token *bp, *lp; + Tokenrow ttr; + int ntokp; + int needspace; + + *narg = -1; /* means that there is no macro call */ + /* look for the ( */ + for (;;) { + trp->tp++; + ntok++; + if (trp->tp >= trp->lp) { + gettokens(trp, 0); + if ((trp->lp-1)->type==END) { + trp->lp -= 1; + trp->tp -= ntok; + return ntok; + } + } + if (trp->tp->type==LP) + break; + if (trp->tp->type!=NL) + return ntok; + } + *narg = 0; + ntok++; + ntokp = ntok; + trp->tp++; + /* search for the terminating ), possibly extending the row */ + needspace = 0; + while (parens>0) { + if (trp->tp >= trp->lp) + gettokens(trp, 0); + if (needspace) { + needspace = 0; + makespace(trp); + } + if (trp->tp->type==END) { + trp->lp -= 1; + trp->tp -= ntok; + error(ERROR, "EOF in macro arglist"); + return ntok; + } + if (trp->tp->type==NL) { + trp->tp += 1; + adjustrow(trp, -1); + trp->tp -= 1; + makespace(trp); + needspace = 1; + continue; + } + if (trp->tp->type==LP) + parens++; + else if (trp->tp->type==RP) + parens--; + trp->tp++; + ntok++; + } + trp->tp -= ntok; + /* Now trp->tp won't move underneath us */ + lp = bp = trp->tp+ntokp; + for (; parens>=0; lp++) { + if (lp->type == LP) { + parens++; + continue; + } + if (lp->type==RP) + parens--; + if (lp->type==DSHARP) + lp->type = DSHARP1; /* ## not special in arg */ + if ((lp->type==COMMA && parens==0) || (parens<0 && (lp-1)->type!=LP)) { + if (*narg>=NARG-1) + error(FATAL, "Sorry, too many macro arguments"); + ttr.bp = ttr.tp = bp; + ttr.lp = lp; + atr[(*narg)++] = normtokenrow(&ttr); + bp = lp+1; + } + } + return ntok; +} + +/* + * substitute the argument list into the replacement string + * This would be simple except for ## and # + */ +void +substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) +{ + Tokenrow tatr; + Token *tp; + int ntok, argno; + + for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) { + if (rtr->tp->type==SHARP) { /* string operator */ + tp = rtr->tp; + rtr->tp += 1; + if ((argno = lookuparg(np, rtr->tp))<0) { + error(ERROR, "# not followed by macro parameter"); + continue; + } + ntok = 1 + (rtr->tp - tp); + rtr->tp = tp; + insertrow(rtr, ntok, stringify(atr[argno])); + continue; + } + if (rtr->tp->type==NAME + && (argno = lookuparg(np, rtr->tp)) >= 0) { + if ((rtr->tp+1)->type==DSHARP + || (rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP)) + insertrow(rtr, 1, atr[argno]); + else { + copytokenrow(&tatr, atr[argno]); + expandrow(&tatr, "<macro>"); + insertrow(rtr, 1, &tatr); + dofree(tatr.bp); + } + continue; + } + rtr->tp++; + } +} + +/* + * Evaluate the ## operators in a tokenrow + */ +void +doconcat(Tokenrow *trp) +{ + Token *ltp, *ntp; + Tokenrow ntr; + int len; + + for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) { + if (trp->tp->type==DSHARP1) + trp->tp->type = DSHARP; + else if (trp->tp->type==DSHARP) { + char tt[128]; + ltp = trp->tp-1; + ntp = trp->tp+1; + if (ltp<trp->bp || ntp>=trp->lp) { + error(ERROR, "## occurs at border of replacement"); + continue; + } + len = ltp->len + ntp->len; + strncpy((char*)tt, (char*)ltp->t, ltp->len); + strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); + tt[len] = '\0'; + setsource("<##>", -1, tt); + maketokenrow(3, &ntr); + gettokens(&ntr, 1); + unsetsource(); + if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) + error(WARNING, "Bad token %r produced by ##", &ntr); + ntr.lp = ntr.bp+1; + trp->tp = ltp; + makespace(&ntr); + insertrow(trp, (ntp-ltp)+1, &ntr); + dofree(ntr.bp); + trp->tp--; + } + } +} + +/* + * tp is a potential parameter name of macro mac; + * look it up in mac's arglist, and if found, return the + * corresponding index in the argname array. Return -1 if not found. + */ +int +lookuparg(Nlist *mac, Token *tp) +{ + Token *ap; + + if (tp->type!=NAME || mac->ap==NULL) + return -1; + for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) { + if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) + return ap - mac->ap->bp; + } + return -1; +} + +/* + * Return a quoted version of the tokenrow (from # arg) + */ +#define STRLEN 512 +Tokenrow * +stringify(Tokenrow *vp) +{ + static Token t = { STRING }; + static Tokenrow tr = { &t, &t, &t+1, 1 }; + Token *tp; + uchar s[STRLEN]; + uchar *sp = s, *cp; + int i, instring; + + *sp++ = '"'; + for (tp = vp->bp; tp < vp->lp; tp++) { + instring = tp->type==STRING || tp->type==CCON; + if (sp+2*tp->len >= &s[STRLEN-10]) { + error(ERROR, "Stringified macro arg is too long"); + break; + } + if (tp->wslen && (tp->flag&XPWS)==0) + *sp++ = ' '; + for (i=0, cp=tp->t; i<tp->len; i++) { + if (instring && (*cp=='"' || *cp=='\\')) + *sp++ = '\\'; + *sp++ = *cp++; + } + } + *sp++ = '"'; + *sp = '\0'; + sp = s; + t.len = strlen((char*)sp); + t.t = newstring(sp, t.len, 0); + return &tr; +} + +/* + * expand a builtin name + */ +void +builtin(Tokenrow *trp, int biname) +{ + char *op; + Token *tp; + Source *s; + + tp = trp->tp; + trp->tp++; + /* need to find the real source */ + s = cursource; + while (s && s->fd==-1) + s = s->next; + if (s==NULL) + s = cursource; + /* most are strings */ + tp->type = STRING; + if (tp->wslen) { + *outp++ = ' '; + tp->wslen = 1; + } + op = outp; + *op++ = '"'; + switch (biname) { + + case KLINENO: + tp->type = NUMBER; + op = outnum(op-1, s->line); + break; + + case KFILE: { + char *src = s->filename; + while ((*op++ = *src++) != 0) + if (src[-1] == '\\') + *op++ = '\\'; + op--; + break; + } + + case KDATE: + strncpy(op, curtime+4, 7); + strncpy(op+7, curtime+20, 4); + op += 11; + break; + + case KTIME: + strncpy(op, curtime+11, 8); + op += 8; + break; + + default: + error(ERROR, "cpp botch: unknown internal macro"); + return; + } + if (tp->type==STRING) + *op++ = '"'; + tp->t = (uchar*)outp; + tp->len = op - outp; + outp = op; +} diff --git a/src/tools/lcc/cpp/nlist.c b/src/tools/lcc/cpp/nlist.c new file mode 100644 index 0000000..d3a8357 --- /dev/null +++ b/src/tools/lcc/cpp/nlist.c @@ -0,0 +1,104 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +extern char *optarg; +extern int optind; +extern int verbose; +extern int Cplusplus; +Nlist *kwdefined; +char wd[128]; + +#define NLSIZE 128 + +static Nlist *nlist[NLSIZE]; + +struct kwtab { + char *kw; + int val; + int flag; +} kwtab[] = { + {"if", KIF, ISKW}, + {"ifdef", KIFDEF, ISKW}, + {"ifndef", KIFNDEF, ISKW}, + {"elif", KELIF, ISKW}, + {"else", KELSE, ISKW}, + {"endif", KENDIF, ISKW}, + {"include", KINCLUDE, ISKW}, + {"define", KDEFINE, ISKW}, + {"undef", KUNDEF, ISKW}, + {"line", KLINE, ISKW}, + {"warning", KWARNING, ISKW}, + {"error", KERROR, ISKW}, + {"pragma", KPRAGMA, ISKW}, + {"eval", KEVAL, ISKW}, + {"defined", KDEFINED, ISDEFINED+ISUNCHANGE}, + {"__LINE__", KLINENO, ISMAC+ISUNCHANGE}, + {"__FILE__", KFILE, ISMAC+ISUNCHANGE}, + {"__DATE__", KDATE, ISMAC+ISUNCHANGE}, + {"__TIME__", KTIME, ISMAC+ISUNCHANGE}, + {"__STDC__", KSTDC, ISUNCHANGE}, + {NULL} +}; + +unsigned long namebit[077+1]; +Nlist *np; + +void +setup_kwtab(void) +{ + struct kwtab *kp; + Nlist *np; + Token t; + static Token deftoken[1] = {{ NAME, 0, 0, 0, 7, (uchar*)"defined" }}; + static Tokenrow deftr = { deftoken, deftoken, deftoken+1, 1 }; + + for (kp=kwtab; kp->kw; kp++) { + t.t = (uchar*)kp->kw; + t.len = strlen(kp->kw); + np = lookup(&t, 1); + np->flag = kp->flag; + np->val = kp->val; + if (np->val == KDEFINED) { + kwdefined = np; + np->val = NAME; + np->vp = &deftr; + np->ap = 0; + } + } +} + +Nlist * +lookup(Token *tp, int install) +{ + unsigned int h; + Nlist *np; + uchar *cp, *cpe; + + h = 0; + for (cp=tp->t, cpe=cp+tp->len; cp<cpe; ) + h += *cp++; + h %= NLSIZE; + np = nlist[h]; + while (np) { + if (*tp->t==*np->name && tp->len==np->len + && strncmp((char*)tp->t, (char*)np->name, tp->len)==0) + return np; + np = np->next; + } + if (install) { + np = new(Nlist); + np->vp = NULL; + np->ap = NULL; + np->flag = 0; + np->val = 0; + np->len = tp->len; + np->name = newstring(tp->t, tp->len, 0); + np->next = nlist[h]; + nlist[h] = np; + quickset(tp->t[0], tp->len>1? tp->t[1]:0); + return np; + } + return NULL; +} diff --git a/src/tools/lcc/cpp/tokens.c b/src/tools/lcc/cpp/tokens.c new file mode 100644 index 0000000..ad9f2f7 --- /dev/null +++ b/src/tools/lcc/cpp/tokens.c @@ -0,0 +1,371 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +static char wbuf[2*OBS]; +static char *wbp = wbuf; + +/* + * 1 for tokens that don't need whitespace when they get inserted + * by macro expansion + */ +static const char wstab[] = { + 0, /* END */ + 0, /* UNCLASS */ + 0, /* NAME */ + 0, /* NUMBER */ + 0, /* STRING */ + 0, /* CCON */ + 1, /* NL */ + 0, /* WS */ + 0, /* DSHARP */ + 0, /* EQ */ + 0, /* NEQ */ + 0, /* LEQ */ + 0, /* GEQ */ + 0, /* LSH */ + 0, /* RSH */ + 0, /* LAND */ + 0, /* LOR */ + 0, /* PPLUS */ + 0, /* MMINUS */ + 0, /* ARROW */ + 1, /* SBRA */ + 1, /* SKET */ + 1, /* LP */ + 1, /* RP */ + 0, /* DOT */ + 0, /* AND */ + 0, /* STAR */ + 0, /* PLUS */ + 0, /* MINUS */ + 0, /* TILDE */ + 0, /* NOT */ + 0, /* SLASH */ + 0, /* PCT */ + 0, /* LT */ + 0, /* GT */ + 0, /* CIRC */ + 0, /* OR */ + 0, /* QUEST */ + 0, /* COLON */ + 0, /* ASGN */ + 1, /* COMMA */ + 0, /* SHARP */ + 1, /* SEMIC */ + 1, /* CBRA */ + 1, /* CKET */ + 0, /* ASPLUS */ + 0, /* ASMINUS */ + 0, /* ASSTAR */ + 0, /* ASSLASH */ + 0, /* ASPCT */ + 0, /* ASCIRC */ + 0, /* ASLSH */ + 0, /* ASRSH */ + 0, /* ASOR */ + 0, /* ASAND */ + 0, /* ELLIPS */ + 0, /* DSHARP1 */ + 0, /* NAME1 */ + 0, /* DEFINED */ + 0, /* UMINUS */ +}; + +void +maketokenrow(int size, Tokenrow *trp) +{ + trp->max = size; + if (size>0) + trp->bp = (Token *)domalloc(size*sizeof(Token)); + else + trp->bp = NULL; + trp->tp = trp->bp; + trp->lp = trp->bp; +} + +Token * +growtokenrow(Tokenrow *trp) +{ + int ncur = trp->tp - trp->bp; + int nlast = trp->lp - trp->bp; + + trp->max = 3*trp->max/2 + 1; + trp->bp = (Token *)realloc(trp->bp, trp->max*sizeof(Token)); + if (trp->bp == NULL) + error(FATAL, "Out of memory from realloc"); + trp->lp = &trp->bp[nlast]; + trp->tp = &trp->bp[ncur]; + return trp->lp; +} + +/* + * Compare a row of tokens, ignoring the content of WS; return !=0 if different + */ +int +comparetokens(Tokenrow *tr1, Tokenrow *tr2) +{ + Token *tp1, *tp2; + + tp1 = tr1->tp; + tp2 = tr2->tp; + if (tr1->lp-tp1 != tr2->lp-tp2) + return 1; + for (; tp1<tr1->lp ; tp1++, tp2++) { + if (tp1->type != tp2->type + || (tp1->wslen==0) != (tp2->wslen==0) + || tp1->len != tp2->len + || strncmp((char*)tp1->t, (char*)tp2->t, tp1->len)!=0) + return 1; + } + return 0; +} + +/* + * replace ntok tokens starting at dtr->tp with the contents of str. + * tp ends up pointing just beyond the replacement. + * Canonical whitespace is assured on each side. + */ +void +insertrow(Tokenrow *dtr, int ntok, Tokenrow *str) +{ + int nrtok = rowlen(str); + + dtr->tp += ntok; + adjustrow(dtr, nrtok-ntok); + dtr->tp -= ntok; + movetokenrow(dtr, str); + makespace(dtr); + dtr->tp += nrtok; + makespace(dtr); +} + +/* + * make sure there is WS before trp->tp, if tokens might merge in the output + */ +void +makespace(Tokenrow *trp) +{ + uchar *tt; + Token *tp = trp->tp; + + if (tp >= trp->lp) + return; + if (tp->wslen) { + if (tp->flag&XPWS + && (wstab[tp->type] || (trp->tp>trp->bp && wstab[(tp-1)->type]))) { + tp->wslen = 0; + return; + } + tp->t[-1] = ' '; + return; + } + if (wstab[tp->type] || (trp->tp>trp->bp && wstab[(tp-1)->type])) + return; + tt = newstring(tp->t, tp->len, 1); + *tt++ = ' '; + tp->t = tt; + tp->wslen = 1; + tp->flag |= XPWS; +} + +/* + * Copy an entire tokenrow into another, at tp. + * It is assumed that there is enough space. + * Not strictly conforming. + */ +void +movetokenrow(Tokenrow *dtr, Tokenrow *str) +{ + int nby; + + /* nby = sizeof(Token) * (str->lp - str->bp); */ + nby = (char *)str->lp - (char *)str->bp; + memmove(dtr->tp, str->bp, nby); +} + +/* + * Move the tokens in a row, starting at tr->tp, rightward by nt tokens; + * nt may be negative (left move). + * The row may need to be grown. + * Non-strictly conforming because of the (char *), but easily fixed + */ +void +adjustrow(Tokenrow *trp, int nt) +{ + int nby, size; + + if (nt==0) + return; + size = (trp->lp - trp->bp) + nt; + while (size > trp->max) + growtokenrow(trp); + /* nby = sizeof(Token) * (trp->lp - trp->tp); */ + nby = (char *)trp->lp - (char *)trp->tp; + if (nby) + memmove(trp->tp+nt, trp->tp, nby); + trp->lp += nt; +} + +/* + * Copy a row of tokens into the destination holder, allocating + * the space for the contents. Return the destination. + */ +Tokenrow * +copytokenrow(Tokenrow *dtr, Tokenrow *str) +{ + int len = rowlen(str); + + maketokenrow(len, dtr); + movetokenrow(dtr, str); + dtr->lp += len; + return dtr; +} + +/* + * Produce a copy of a row of tokens. Start at trp->tp. + * The value strings are copied as well. The first token + * has WS available. + */ +Tokenrow * +normtokenrow(Tokenrow *trp) +{ + Token *tp; + Tokenrow *ntrp = new(Tokenrow); + int len; + + len = trp->lp - trp->tp; + if (len<=0) + len = 1; + maketokenrow(len, ntrp); + for (tp=trp->tp; tp < trp->lp; tp++) { + *ntrp->lp = *tp; + if (tp->len) { + ntrp->lp->t = newstring(tp->t, tp->len, 1); + *ntrp->lp->t++ = ' '; + if (tp->wslen) + ntrp->lp->wslen = 1; + } + ntrp->lp++; + } + if (ntrp->lp > ntrp->bp) + ntrp->bp->wslen = 0; + return ntrp; +} + +/* + * Debugging + */ +void +peektokens(Tokenrow *trp, char *str) +{ + Token *tp; + + tp = trp->tp; + flushout(); + if (str) + fprintf(stderr, "%s ", str); + if (tp<trp->bp || tp>trp->lp) + fprintf(stderr, "(tp offset %ld) ", (long int) (tp - trp->bp)); + for (tp=trp->bp; tp<trp->lp && tp<trp->bp+32; tp++) { + if (tp->type!=NL) { + int c = tp->t[tp->len]; + tp->t[tp->len] = 0; + fprintf(stderr, "%s", tp->t); + tp->t[tp->len] = c; + } + if (tp->type==NAME) { + fprintf(stderr, tp==trp->tp?"{*":"{"); + prhideset(tp->hideset); + fprintf(stderr, "} "); + } else + fprintf(stderr, tp==trp->tp?"{%x*} ":"{%x} ", tp->type); + } + fprintf(stderr, "\n"); + fflush(stderr); +} + +void +puttokens(Tokenrow *trp) +{ + Token *tp; + int len, wlen; + uchar *p; + + if (verbose) + peektokens(trp, ""); + tp = trp->bp; + for (; tp<trp->lp; tp++) { + len = tp->len+tp->wslen; + p = tp->t-tp->wslen; + while (tp<trp->lp-1 && p+len == (tp+1)->t - (tp+1)->wslen) { + tp++; + len += tp->wslen+tp->len; + } + if (len>OBS/2) { /* handle giant token */ + if (wbp > wbuf) + wlen = write(1, wbuf, wbp-wbuf); + wlen = write(1, (char *)p, len); + wbp = wbuf; + } else { + memcpy(wbp, p, len); + wbp += len; + } + if (wbp >= &wbuf[OBS]) { + wlen = write(1, wbuf, OBS); + if (wbp > &wbuf[OBS]) + memcpy(wbuf, wbuf+OBS, wbp - &wbuf[OBS]); + wbp -= OBS; + } + } + trp->tp = tp; + if (cursource->fd==0) + flushout(); +} + +void +flushout(void) +{ + int wlen; + if (wbp>wbuf) { + wlen = write(1, wbuf, wbp-wbuf); + wbp = wbuf; + } +} + +/* + * turn a row into just a newline + */ +void +setempty(Tokenrow *trp) +{ + trp->tp = trp->bp; + trp->lp = trp->bp+1; + *trp->bp = nltoken; +} + +/* + * generate a number + */ +char * +outnum(char *p, int n) +{ + if (n>=10) + p = outnum(p, n/10); + *p++ = n%10 + '0'; + return p; +} + +/* + * allocate and initialize a new string from s, of length l, at offset o + * Null terminated. + */ +uchar * +newstring(uchar *s, int l, int o) +{ + uchar *ns = (uchar *)domalloc(l+o+1); + + ns[l+o] = '\0'; + return (uchar*)strncpy((char*)ns+o, (char*)s, l) - o; +} diff --git a/src/tools/lcc/cpp/unix.c b/src/tools/lcc/cpp/unix.c new file mode 100644 index 0000000..17986d8 --- /dev/null +++ b/src/tools/lcc/cpp/unix.c @@ -0,0 +1,128 @@ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "cpp.h" + +extern int lcc_getopt(int, char *const *, const char *); +extern char *optarg, rcsid[]; +extern int optind; +int verbose; +int Mflag; /* only print active include files */ +char *objname; /* "src.$O: " */ +int Cplusplus = 1; + +void +setup(int argc, char **argv) +{ + int c, fd, i; + char *fp, *dp; + Tokenrow tr; + extern void setup_kwtab(void); + uchar *includeDirs[ NINCLUDE ] = { 0 }; + int numIncludeDirs = 0; + + setup_kwtab(); + while ((c = lcc_getopt(argc, argv, "MNOVv+I:D:U:F:lg")) != -1) + switch (c) { + case 'N': + for (i=0; i<NINCLUDE; i++) + if (includelist[i].always==1) + includelist[i].deleted = 1; + break; + case 'I': + includeDirs[ numIncludeDirs++ ] = newstring( (uchar *)optarg, strlen( optarg ), 0 ); + break; + case 'D': + case 'U': + setsource("<cmdarg>", -1, optarg); + maketokenrow(3, &tr); + gettokens(&tr, 1); + doadefine(&tr, c); + unsetsource(); + break; + case 'M': + Mflag++; + break; + case 'v': + fprintf(stderr, "%s %s\n", argv[0], rcsid); + break; + case 'V': + verbose++; + break; + case '+': + Cplusplus++; + break; + default: + break; + } + dp = "."; + fp = "<stdin>"; + fd = 0; + if (optind<argc) { + dp = basepath( argv[optind] ); + fp = (char*)newstring((uchar*)argv[optind], strlen(argv[optind]), 0); + if ((fd = open(fp, 0)) <= 0) + error(FATAL, "Can't open input file %s", fp); + } + if (optind+1<argc) { + int fdo = creat(argv[optind+1], 0666); + if (fdo<0) + error(FATAL, "Can't open output file %s", argv[optind+1]); + dup2(fdo, 1); + } + if(Mflag) + setobjname(fp); + includelist[NINCLUDE-1].always = 0; + includelist[NINCLUDE-1].file = dp; + + for( i = 0; i < numIncludeDirs; i++ ) + appendDirToIncludeList( (char *)includeDirs[ i ] ); + + setsource(fp, fd, NULL); +} + + +char *basepath( char *fname ) +{ + char *dp = "."; + char *p; + if ((p = strrchr(fname, '/')) != NULL) { + int dlen = p - fname; + dp = (char*)newstring((uchar*)fname, dlen+1, 0); + dp[dlen] = '\0'; + } + + return dp; +} + +/* memmove is defined here because some vendors don't provide it at + all and others do a terrible job (like calling malloc) */ +// -- ouch, that hurts -- ln +#ifndef MACOS_X /* always use the system memmove() on Mac OS X. --ryan. */ +#ifdef memmove +#undef memmove +#endif +void * +memmove(void *dp, const void *sp, size_t n) +{ + unsigned char *cdp, *csp; + + if (n<=0) + return 0; + cdp = dp; + csp = (unsigned char *)sp; + if (cdp < csp) { + do { + *cdp++ = *csp++; + } while (--n); + } else { + cdp += n; + csp += n; + do { + *--cdp = *--csp; + } while (--n); + } + return 0; +} +#endif diff --git a/src/tools/lcc/doc/4.html b/src/tools/lcc/doc/4.html new file mode 100644 index 0000000..c36f280 --- /dev/null +++ b/src/tools/lcc/doc/4.html @@ -0,0 +1,754 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> + +<head> +<link HREF="mailto:drh@microsoft.com" REV="made" TITLE="David R. Hanson"> +<title>The lcc 4.1 Code-Generation Interface</title> +</head> + +<body> + +<h1>The lcc 4.1 Code-Generation Interface</h1> + +<p ALIGN="LEFT"><strong><a HREF="http://www.research.microsoft.com/~cwfraser/">Christopher +W. Fraser</a> and <a HREF="http://www.research.microsoft.com/~drh/">David R. Hanson</a>, <a +HREF="http://www.research.microsoft.com/">Microsoft Research</a></strong></p> + +<h2>Contents</h2> + +<dir> + <li><a HREF="#intro">Introduction</a> </li> + <li><a HREF="#metrics">5.1 Type Metrics</a></li> + <li><a HREF="#symbols">5.3 Symbols</a> </li> + <li><a HREF="#operators">5.5 Dag Operators</a></li> + <li><a HREF="#flags">5.6 Interface Flags</a></li> + <li><a HREF="#definitions">5.8 Definitions</a></li> + <li><a HREF="#constants">5.9 Constants</a></li> + <li><a HREF="#upcalls">5.12 Upcalls</a></li> +</dir> + +<h2><a NAME="intro">Introduction</a></h2> + +<p>Version 4.1 is the latest release of <a +HREF="http://www.cs.princeton.edu/software/lcc/">lcc</a>, the ANSI C compiler described in +our book <cite>A Retargetable C Compiler: Design and Implementation</cite> +(Addison-Wesley, 1995, ISBN 0-8053-1670-1). This document summarizes the differences +between the 4.1 code-generation interface and the 3.x interface described in Chap. 5 of <cite>A +Retargetable C Compiler</cite>.</p> + +<p>Previous versions of lcc supported only three sizes of integers, two sizes of floats, +and insisted that pointers fit in unsigned integers (see Sec. 5.1 of <cite>A Retargetable +C Compiler</cite>). These assumptions simplified the compiler, and were suitable for +32-bit architectures. But on 64-bit architectures, such as the DEC ALPHA, it's natural to +have four sizes of integers and perhaps three sizes of floats, and on 16-bit +architectures, 32-bit pointers don't fit in unsigned integers. Also, the 3.x constaints +limited the use of lcc's back ends for other languages, such as Java.</p> + +<p>Version 4.x removes all of these restrictions: It supports any number of sizes for +integers and floats, and the size of pointers need not be related to the size of any of +the integer types. The major changes in the code-generation interface are: + +<ul> + <li>The number of type suffixes has been reduced to 6.</li> + <li>Dag operators are composed of a generic operator, a type suffix, and a size.</li> + <li>Unsigned variants of several operators have been added.</li> + <li>Several interface functions have new signatures.</li> +</ul> + +<p>In addition, version 4.x is written in ANSI C and uses the standard I/O library and +other standard C functions.</p> + +<p>The sections below parallel the subsections of Chap. 5 of <cite>A Retargetable C +Compiler</cite> and summarize the differences between the 3.x and 4.x code-generation +interface. Unaffected subsections are omitted. Page citations refer to pages in <cite>A +Retargetable C Compiler</cite>.</p> + +<h2><a NAME="metrics">5.1 Type Metrics</a></h2> + +<p>There are now 10 metrics in an interface record:</p> + +<pre>Metrics charmetric; +Metrics shortmetric; +Metrics intmetric; +Metrics longmetric; +Metrics longlongmetric; +Metrics floatmetric; +Metrics doublemetric; +Metrics longdoublemetric; +Metrics ptrmetric; +Metrics structmetric;</pre> + +<p>Each of these specifies the size and alignment of the corresponding type. <code>ptrmetric</code> +describes all pointers.</p> + +<h2><a NAME="symbols">5.3 Symbols</a></h2> + +<p>The actual value of a constant is stored in the <code>u.c.v</code> field of a symbol, +which holds a <code>Value</code>:</p> + +<pre>typedef union value { + long i; + unsigned long u; + long double d; + void *p; + void (*g)(void); +} Value;</pre> + +<p>The value is stored in the appropriate field according to its type, which is given by +the symbol's <code>type</code> field.</p> + +<h2><a NAME="operators">5.5 Dag Operators</a></h2> + +<p>The <code>op</code> field a of <code>node</code> structure holds a dag operator, which +consists of a generic operator, a type suffix, and a size indicator. The type suffixes +are:</p> + +<pre>enum { + F=FLOAT, + I=INT, + U=UNSIGNED, + P=POINTER, + V=VOID, + B=STRUCT +}; + +#define sizeop(n) ((n)<<10)</pre> + +<p>Given a generic operator <code>o</code>, a type suffix <code>t</code>, and a size <code>s</code>, +a type- and size-specific operator is formed by <code>o+t+sizeop(s)</code>. For example, <code>ADD+F+sizeop(4)</code> +forms the operator <code>ADDF4</code>, which denotes the sum of two 4-byte floats. +Similarly, <code>ADD+F+sizeop(8)</code> forms <code>ADDF8</code>, which denotes 8-byte +floating addition. In the 3.x code-generation interface, <code>ADDF</code> and <code>ADDD</code> +denoted these operations. There was no size indicator in the 3.x operators because the +type suffix supplied both a type and a size.</p> + +<p>Table 5.1 lists each generic operator, its valid type suffixes, and the number of <code>kids</code> +and <code>syms</code> that it uses; multiple values for <code>kids</code> indicate +type-specific variants. The notations in the <strong>syms</strong> column give the number +of <code>syms</code> values and a one-letter code that suggests their uses: 1V indicates +that <code>syms[0]</code> points to a symbol for a variable, 1C indicates that <code>syms[0]</code> +is a constant, and 1L indicates that <code>syms[0]</code> is a label. For 1S, <code>syms[0]</code> +is a constant whose value is a size in bytes; 2S adds <code>syms[1]</code>, which is a +constant whose value is an alignment. For most operators, the type suffix and size +indicator denote the type and size of operation to perform and the type and size of the +result.</p> + +<table WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0"> + <tr> + <td COLSPAN="6" ALIGN="CENTER"><strong>Table 5.1<img SRC="/~drh/resources/dot_clear.gif" + ALT="|" WIDTH="18" HEIGHT="1">Node Operators.</strong></td> + </tr> + <tr> + <td><strong>syms</strong></td> + <td><strong>kids</strong></td> + <td><strong>Operator</strong></td> + <td><strong>Type Suffixes</strong></td> + <td><strong>Sizes</strong></td> + <td><strong>Operation</strong></td> + </tr> + <tr> + <td>1V</td> + <td>0</td> + <td><code>ADDRF</code></td> + <td><code>...P..</code></td> + <td>p</td> + <td>address of a parameter</td> + </tr> + <tr> + <td>1V</td> + <td>0</td> + <td><code>ADDRG</code></td> + <td><code>...P..</code></td> + <td>p</td> + <td>address of a global</td> + </tr> + <tr> + <td>1V</td> + <td>0</td> + <td><code>ADDRL</code></td> + <td><code>...P..</code></td> + <td>p</td> + <td>address of a local</td> + </tr> + <tr> + <td>1C</td> + <td>0</td> + <td><code>CNST</code></td> + <td><code>FIUP..</code></td> + <td>fdx csilh p</td> + <td>constant</td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="1" HEIGHT="12"></td> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>1</td> + <td><code>BCOM</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>bitwise complement</td> + </tr> + <tr> + <td>1S</td> + <td>1</td> + <td><code>CVF</code></td> + <td><code>FI....</code></td> + <td>fdx ilh</td> + <td>convert from float</td> + </tr> + <tr> + <td>1S</td> + <td>1</td> + <td><code>CVI</code></td> + <td><code>FIU...</code></td> + <td>fdx csilh csilhp</td> + <td>convert from signed integer</td> + </tr> + <tr> + <td>1S</td> + <td>1</td> + <td><code>CVP</code></td> + <td><code>..U..</code></td> + <td>p</td> + <td>convert from pointer</td> + </tr> + <tr> + <td>1S</td> + <td>1</td> + <td><code>CVU</code></td> + <td><code>.IUP..</code></td> + <td>csilh p</td> + <td>convert from unsigned integer</td> + </tr> + <tr> + <td></td> + <td>1</td> + <td><code>INDIR</code></td> + <td><code>FIUP.B</code></td> + <td>fdx csilh p</td> + <td>fetch</td> + </tr> + <tr> + <td></td> + <td>1</td> + <td><code>NEG</code></td> + <td><code>FI....</code></td> + <td>fdx ilh</td> + <td>negation</td> + </tr> + <tr> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="1" HEIGHT="12"></td> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>ADD</code></td> + <td><code>FIUP..</code></td> + <td>fdx ilh ilhp p</td> + <td>addition</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>BAND</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>bitwise AND</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>BOR</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>bitwise inclusive OR</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>BXOR</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>bitwise exclusive OR</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>DIV</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh</td> + <td>division</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>LSH</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>left shift</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>MOD</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>modulus</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>MUL</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh</td> + <td>multiplication</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>RSH</code></td> + <td><code>.IU...</code></td> + <td>ilh</td> + <td>right shift</td> + </tr> + <tr> + <td></td> + <td>2</td> + <td><code>SUB</code></td> + <td><code>FIUP..</code></td> + <td>fdx ilh ilhp p</td> + <td>subtraction</td> + </tr> + <tr> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="1" HEIGHT="12"></td> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>2S</td> + <td>2</td> + <td><code>ASGN</code></td> + <td><code>FIUP.B</code></td> + <td>fdx csilh p</td> + <td>assignment</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>EQ</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if equal</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>GE</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if greater than or equal</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>GT</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if greater than</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>LE</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if less than or equal</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>LT</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if less than</td> + </tr> + <tr> + <td>1L</td> + <td>2</td> + <td><code>NE</code></td> + <td><code>FIU...</code></td> + <td>fdx ilh ilhp</td> + <td>jump if not equal</td> + </tr> + <tr> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>2S</td> + <td>1</td> + <td><code>ARG</code></td> + <td><code>FIUP.B</code></td> + <td>fdx ilh p</td> + <td>argument</td> + </tr> + <tr> + <td>1</td> + <td>1 or 2</td> + <td><code>CALL</code></td> + <td><code>FIUPVB</code></td> + <td>fdx ilh p</td> + <td>function call</td> + </tr> + <tr> + <td></td> + <td>1</td> + <td><code>RET</code></td> + <td><code>FIUPV.</code></td> + <td>fdx ilh p</td> + <td>return from function</td> + </tr> + <tr> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="1" HEIGHT="12"></td> + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>1</td> + <td><code>JUMP</code></td> + <td><code>....V.</code></td> + <td></td> + <td>unconditional jump</td> + </tr> + <tr> + <td>1L</td> + <td>0</td> + <td><code>LABEL</code></td> + <td><code>....V.</code></td> + <td></td> + <td>label definition</td> + </tr> +</table> + +<p>The entries in the <strong>Sizes</strong> column indicate sizes of the operators that +back ends must implement. Letters denote the size of float (f), double (d), long double +(x), character (c), short integer (s), integer (i), long integer (l), "long +long" integer (h) , and pointer (p). These sizes are separated into sets for each +type suffix, except that a single set is used for both I and U when the set for I is +identical to the set for U.</p> + +<p>The actual values for the size indicators, fdxcsilhp, depend on the target. A +specification like <code>ADDF</code>f denotes the operator <code>ADD+F+sizeop(</code>f<code>)</code>, +where "f" is replaced by a target-dependent value, e.g., <code>ADDF4</code> and <code>ADDF8</code>. +For example, back ends must implement the following <code>CVI</code> and <code>MUL</code> +operators.</p> + +<blockquote> + <p><code>CVIF</code>f <code>CVIF</code>d <code>CVIF</code>x<br> + <code>CVII</code>c <code>CVII</code>s <code>CVII</code>i <code>CVII</code>l <code>CVII</code>h<br> + <code>CVIU</code>c <code>CVIU</code>s <code>CVIU</code>i <code>CVIU</code>l <code>CVIU</code>h + <code>CVIU</code>p<br> + <br> + <code>MULF</code>f <code>MULF</code>d <code>MULF</code>x<br> + <code>MULI</code>i <code>MULI</code>l <code>MULI</code>h<br> + <code>MULU</code>i <code>MULU</code>l <code>MULU</code>h</p> +</blockquote> + +<p>On most platforms, there are fewer than three sizes of floats and six sizes of +integers, and pointers are usually the same size as one of the integers. And lcc doesn't +support the "long long" type, so h is not currently used. So the set of +platform-specific operators is usually smaller than the list above suggests. For example, +the X86, SPARC, and MIPS back ends implement the following <code>CVI</code> and <code>MUL</code> +operators.</p> + +<blockquote> + <p><code>CVIF</code>4 <code>CVIF</code>8<br> + <code>CVII</code>1 <code>CVII</code>2 <code>CVII</code>4<br> + <code>CVIU</code>1 <code>CVIU</code>2 <code>CVIU</code>4 <br> + <br> + <code>MULF</code>4 <code>MULF</code>8<br> + <code>MULI</code>4<br> + <code>MULU</code>4</p> +</blockquote> + +<p>The set of operators is thus target-dependent; for example, <code>ADDI8</code> appears +only if the target supports an 8-byte integer type. <a +HREF="ftp://ftp.cs.princeton.edu/pub/packages/lcc/contrib/ops.c"><code>ops.c</code></a> is +a program that, given a set of sizes, prints the required operators and their values, +e.g.,</p> + +<blockquote> + <pre>% <em>ops c=1 s=2 i=4 l=4 h=4 f=4 d=8 x=8 p=4</em> +... + CVIF4=4225 CVIF8=8321 + CVII1=1157 CVII2=2181 CVII4=4229 + CVIU1=1158 CVIU2=2182 CVIU4=4230 +... + MULF4=4561 MULF8=8657 + MULI4=4565 + MULU4=4566 +... +131 operators</pre> +</blockquote> + +<p>The type suffix for a conversion operator denotes the type of the result and the size +indicator gives the size of the result. For example, <code>CVUI4</code> converts an +unsigned (<code>U</code>) to a 4-byte signed integer (<code>I4</code>). The <code>syms[0]</code> +field points to a symbol-table entry for a integer constant that gives the size of the +source operand. For example, if <code>syms[0]</code> in a <code>CVUI4</code> points to a +symbol-table entry for 2, the conversion widens a 2-byte unsigned integer to a 4-byte +signed integer. Conversions that widen unsigned integers zero-extend; those that widen +signed integers sign-extend.</p> + +<p>The front end composes conversions between types <em>T</em><sub>1</sub> and <em>T</em><sub>2</sub> +by widening <em>T</em><sub>1</sub> to its "supertype", if necessary, converting +that result to <em>T</em><sub>2</sub>'s supertype, then narrowing the result to <em>T</em><sub>2</sub>, +if necessary. The following table lists the supertypes; omitted entries are their own +supertypes.</p> + +<blockquote> + <table BORDER="0" CELLPADDING="0" CELLSPACING="0"> + <tr> + <td><strong>Type</strong></td> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="24" HEIGHT="1"></td> + <td><strong>Supertype</strong></td> + </tr> + <tr> + <td>signed char</td> + <td></td> + <td>int</td> + </tr> + <tr> + <td>signed short</td> + <td></td> + <td>int</td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td>unsigned char</td> + <td></td> + <td>int, if sizeof (char) < sizeof (int)<br> + unsigned, otherwise</td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td>unsigned short</td> + <td></td> + <td>int, if sizeof (short) < sizeof (int)<br> + unsigned, otherwise</td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td>void *</td> + <td></td> + <td>an unsigned type as large as a pointer</td> + </tr> + </table> +</blockquote> + +<p>Pointers are converted to an unsigned type of the same size, even when that type is not +one of the integer types.</p> + +<p>For example, the front end converts a signed short to a float by first converting it to +an int and then to a float. It converts an unsigned short to an int with a single <code>CVUI</code>i +conversion, when shorts are smaller than ints.</p> + +<p>There are now signed and unsigned variants of <code>ASGN</code>, <code>INDIR</code>, <code>BCOM</code>, +<code>BOR</code>, <code>BXOR</code>, <code>BAND</code>, <code>ARG</code>, <code>CALL</code>, +and <code>RET</code> to simplify code generation on platforms that use different +instructions or register set for signed and unsigned operations. Likewise there are now +pointer variants of <code>ASGN</code>, <code>INDIR</code>, <code>ARG</code>, <code>CALL</code>, +and <code>RET</code>.</p> + +<h2><a NAME="flags">5.6 Interface Flags</a></h2> + +<pre>unsigned unsigned_char:1;</pre> + +<p>tells the front end whether plain characters are signed or unsigned. If it's zero, char +is a signed type; otherwise, char is an unsigned type.</p> + +<p>All the interface flags can be set by command-line options, e.g., <code>-Wf-unsigned_char=1</code> +causes plain characters to be unsigned.</p> + +<h2><a NAME="definitions">5.8 Definitions</a></h2> + +<p>The front end announces local variables by calling</p> + +<pre>void (*local)(Symbol);</pre> + +<p>It announces temporaries likewise; these have the symbol's <code>temporary</code> flag +set, which indicates that the symbol will be used only in the next call to <code>gen</code>. +If a temporary's <code>u.t.cse</code> field is nonnull, it points to the node that +computes the value assigned to the temporary; see page 346.</p> + +<p>The front end calls</p> + +<pre>void (*address)(Symbol p, Symbol q, long n);</pre> + +<p>to initialize <code>q</code> to a symbol that represents an address of the form <em>x</em>+<code>n</code>, +where <em>x</em> is the address represented by <code>p</code> and the long integer <code>n</code> +is positive or negative.</p> + +<h2><a NAME="constants">5.9 Constants</a></h2> + +<p>The interface function</p> + +<pre>void (*defconst)(int suffix, int size, Value v);</pre> + +<p>initializes constants. defconst emits directives to define a cell and initialize it to +a constant value. v is the constant value, suffix identifies the type of the value, and +size is the size of the value in bytes. The value of suffix indicates which field of v +holds the value, as shown in the following table.</p> + +<blockquote> + <table BORDER="0" CELLPADDING="1" CELLSPACING="1"> + <tr> + <td><strong>suffix</strong></td> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="24" HEIGHT="1"></td> + <td><strong>v Field</strong></td> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="24" HEIGHT="1"></td> + <td><strong>size</strong></td> + </tr> + <tr> + <td><code>F</code></td> + <td></td> + <td><code>v.d</code></td> + <td></td> + <td>float, double, long double</td> + </tr> + <tr> + <td><code>I</code></td> + <td></td> + <td><code>v.i</code></td> + <td></td> + <td>signed char, signed short, signed int, signed long</td> + </tr> + <tr> + <td><code>U</code></td> + <td></td> + <td><code>v.u</code></td> + <td></td> + <td>unsigned char, unsigned short, unsigned int, unsigned long</td> + </tr> + <tr> + <td><code>P</code></td> + <td></td> + <td><code>v.p</code></td> + <td></td> + <td>void *</td> + </tr> + </table> +</blockquote> + +<p><code>defconst</code> must narrow <code>v.</code>x when <code>size</code> is less than <code>sizeof</code> +<code>v.</code>x; e.g., to emit an unsigned char, <code>defconst</code> should emit <code>(unsigned +char)v.i</code>.</p> + +<h2><a NAME="upcalls">5.12 Upcalls</a></h2> + +<p>lcc 4.x uses standard I/O and its I/O functions have been changed accordingly. lcc +reads input from the standard input, emits code to the standard output, and writes +diagnostics to the standard error output. It uses <code>freopen</code> to redirect these +streams to explicit files, when necessary.</p> + +<p><code>bp</code>, <code>outflush</code>, and <code>outs</code> have been eliminated.</p> + +<pre>extern void fprint(FILE *f, const char *fmt, ...); +extern void print(const char *fmt, ...);</pre> + +<p>print formatted data to file <code>f</code> (<code>fprint</code>) or the standard +output (<code>print</code>). These functions are like standard C's <code>printf</code> and +<code>fprintf</code>, but support only some of the standard conversion specifiers and do +not support flags, precision, and field-width specifications. They support the following +new conversion specifiers in addition to those described on page 99.</p> + +<blockquote> + <table BORDER="0" CELLPADDING="0" CELLSPACING="0"> + <tr> + <td><strong>Specifiers</strong></td> + <td><img SRC="/~drh/resources/dot_clear.gif" ALT="|" WIDTH="24" HEIGHT="1"></td> + <td><strong>Corresponding printf Specifiers</strong></td> + </tr> + <tr> + <td><code>%c</code></td> + <td></td> + <td><code>%c</code></td> + </tr> + <tr> + <td><code>%d %D</code></td> + <td></td> + <td><code>%d %ld</code></td> + </tr> + <tr> + <td><code>%u %U</code></td> + <td></td> + <td><code>%u %lu</code></td> + </tr> + <tr> + <td><code>%x %X</code></td> + <td></td> + <td><code>%x %lx</code></td> + </tr> + <tr> + <td><code>%f %e %g</code></td> + <td></td> + <td><code>%e %f %g</code></td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td><code>%p</code></td> + <td></td> + <td>Converts the corresponding void * argument to unsigned long and prints it with the <code>printf</code> + <code>%#x</code> specifier or just <code>%x</code> when the argument is null.</td> + </tr> + <tr ALIGN="LEFT" VALIGN="TOP"> + <td><code>%I</code></td> + <td></td> + <td>Prints the number of spaces given by the corresponding argument.</td> + </tr> + </table> +</blockquote> + +<pre>#define generic(op) ((op)&0x3F0) +#define specific(op) ((op)&0x3FF)</pre> + +<p><code>generic(op)</code> returns the generic variant of <code>op</code>; that is, +without its type suffix and size indicator. <code>specific(op)</code> returns the +type-specific variant of <code>op</code>; that is, without its size indicator.</p> + +<p><code>newconst</code> has been replaced by</p> + +<pre>extern Symbol intconst(int n);</pre> + +<p>which installs the integer constant <code>n</code> in the symbol table, if necessary, +and returns a pointer to the symbol-table entry.</p> + +<hr> + +<address> + <a HREF="http://www.research.microsoft.com/~cwfraser/">Chris Fraser</a> / <a + HREF="mailto:cwfraser@microsoft.com">cwfraser@microsoft.com</a><br> + <a HREF="http://www.research.microsoft.com/~drh/">David Hanson</a> / <a + HREF="mailto:drh@microsoft.com">drh@microsoft.com</a><br> + $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ +</address> +</body> +</html> diff --git a/src/tools/lcc/doc/bprint.1 b/src/tools/lcc/doc/bprint.1 new file mode 100644 index 0000000..8cf9971 --- /dev/null +++ b/src/tools/lcc/doc/bprint.1 @@ -0,0 +1,83 @@ +.\" $Id: bprint.1 145 2001-10-17 21:53:10Z timo $ +.TH BPRINT 1 "local \- $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $" +.SH NAME +bprint \- expression profiler +.SH SYNOPSIS +.B bprint +[ +.I option ... +] +[ +.I file ... +] +.SH DESCRIPTION +.I bprint +produces on the standard output a listing of the programs compiled by +.I lcc +with the +.B \-b +option. +Executing an +.B a.out +so compiled appends profiling data to +.BR prof.out . +The first token of each expression in the listing is preceded +by the number of times it was executed +enclosed in angle brackets as determined from the data in +.BR prof.out . +.I bprint +interprets the following options. +.TP +.B \-c +Compress the +.B prof.out +file, which otherwise grows with every execution of +.BR a.out . +.TP +.B \-b +Print an annotated listing as described above. +.TP +.B \-n +Include line numbers in the listing. +.TP +.B \-f +Print only the number of invocations of each function. +A second +.B \-f +summarizes call sites instead of callers. +.TP +.BI \-I \*Sdir +specifies additional directories in which to seek +files given in +.B prof.out +that do not begin with `/'. +.PP +If any file names are given, only the requested data for those files are printed +in the order presented. +If no options are given, +.B \-b +is assumed. +.SH FILES +.PP +.ta \w'$LCCDIR/liblcc.{a,lib}XX'u +.nf +prof.out profiling data +$LCCDIR/liblcc.{a,lib} \fIlcc\fP-specific library +.SH "SEE ALSO" +.IR lcc (1), +.IR prof (1) +.SH BUGS +Macros and comments can confuse +.I bprint +because it uses post-expansion source coordinates +to annotate pre-expansion source files. +If +.I bprint +sees that it's about to print a statement count +.I inside +a number or identifier, it moves the count to just +.I before +the token. +.PP +Can't cope with an ill-formed +.BR prof.out . diff --git a/src/tools/lcc/doc/bprint.pdf b/src/tools/lcc/doc/bprint.pdf Binary files differnew file mode 100644 index 0000000..1b11963 --- /dev/null +++ b/src/tools/lcc/doc/bprint.pdf diff --git a/src/tools/lcc/doc/install.html b/src/tools/lcc/doc/install.html new file mode 100644 index 0000000..3cc59a8 --- /dev/null +++ b/src/tools/lcc/doc/install.html @@ -0,0 +1,796 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> + +<head> +<link HREF="mailto:drh@cs.princeton.edu" REV="made" TITLE="David R. Hanson"> +<title>Installing lcc</title> +</head> + +<body> + +<h1>Installing lcc</h1> + +<p ALIGN="LEFT"><strong><a HREF="http://www.research.microsoft.com/~cwfraser/">Christopher +W. Fraser</a> and <a HREF="http://www.research.microsoft.com/~drh/">David R. Hanson</a>, <a +HREF="http://www.research.microsoft.com/">Microsoft Research</a></strong></p> + +<h2>Contents</h2> + +<dir> + <li><a HREF="#intro">Introduction</a></li> + <li><a HREF="#unix">Installation on UNIX</a></li> + <li><a HREF="#driver">Building the Driver</a></li> + <li><a HREF="#rcc">Building the Compiler and Accessories</a></li> + <li><a HREF="#win32">Installation on Windows NT 4.0 and Windows 95/98</a></li> + <li><a HREF="#bugs">Reporting Bugs</a></li> + <li><a HREF="#mailinglist">Keeping in Touch</a></li> +</dir> + +<h2><a NAME="intro">Introduction</a></h2> + +<p><a HREF="http://www.cs.princeton.edu/software/lcc/">lcc</a> is the ANSI C compiler +described in our book <cite>A Retargetable C Compiler: Design and Implementation</cite> +(Addison-Wesley, 1995, ISBN 0-8053-1670-1).</p> + +<p>If you're installing lcc on a UNIX system, read the remainder of this section and +continue with the next section. If you're installing lcc on a Windows NT 4.0 or Windows +95/98 system, and you intend only to <u>use</u> lcc, you can run the <a +href="ftp://ftp.cs.princeton.edu/pub/packages/lcc/lcc41.exe">InstallShield executable</a>, +which installs the binaries and the documentation. If you want to <u>modify</u> lcc or <u>rebuild</u> +it from the source files, you need the <a +href="ftp://ftp.cs.princeton.edu/packages/lcc/lcc41.zip">complete distribution</a>, and +you should read the rest of the section, the following three sections, and the <a +HREF="#win32">Windows NT/95/98</a> section.</p> + +<p>Extract the distribution into its own directory. All non-absolute paths below are +relative to this directory. The distribution holds the following subdirectories.</p> + +<blockquote> + <table BORDER="0" CELLPADDING="1" CELLSPACING="1" WIDTH="80%"> + <tr> + <td><a HREF="../src"><code>src</code></a></td> + <td></td> + <td>source code</td> + </tr> + <tr> + <td><a HREF="../etc"><code>etc</code></a></td> + <td></td> + <td>driver, accessories</td> + </tr> + <tr> + <td><a HREF="../lib"><code>lib</code></a></td> + <td></td> + <td>runtime library source code</td> + </tr> + <tr> + <td><a HREF="../cpp"><code>cpp</code></a></td> + <td></td> + <td>preprocessor source code</td> + </tr> + <tr> + <td><a HREF="../lburg"><code>lburg</code></a></td> + <td></td> + <td>code-generator generator source code</td> + </tr> + <tr> + <td><a HREF="../doc"><code>doc</code></a></td> + <td></td> + <td>this document, man pages</td> + </tr> + <tr> + <td><code><a HREF="../include">include</a>/*/*</code></td> + <td></td> + <td>include files</td> + </tr> + <tr> + <td><a HREF="../tst"><code>tst</code></a></td> + <td></td> + <td>test suite</td> + </tr> + <tr> + <td><code><a HREF="../alpha">alpha</a>/*/tst</code></td> + <td></td> + <td>ALPHA test outputs</td> + </tr> + <tr> + <td><code><a HREF="../mips">mips</a>/*/tst</code></td> + <td></td> + <td>MIPS test outputs</td> + </tr> + <tr> + <td><code><a HREF="../sparc">sparc</a>/*/tst</code></td> + <td></td> + <td>SPARC test outputs</td> + </tr> + <tr> + <td><code><a HREF="../x86">x86</a>/*/tst</code></td> + <td></td> + <td>X86 test outputs</td> + </tr> + </table> +</blockquote> + +<p><code>doc/install.html</code> is the HTML file for this document. <a HREF="4.html"><code>doc/4.html</code></a> +describes the internal differences between lcc 3.x and 4.1.</p> + +<p>The installation makefile is designed so that lcc can be installed from a read-only +file system or directory, which is common in networked environments, so the distribution +can be unloaded on a central file server. <strong>You will need an existing ANSI/ISO C +compiler to build and install lcc.</strong></p> + +<h2><a NAME="unix">Installation on UNIX</a></h2> + +<p>The compilation components (the preprocessor, include files, and compiler proper, etc.) +are installed in a single <em>build directory</em>. On multi-platform systems supported by +a central file server, it's common to store the build directory in a location specific to +the platform and to the version of lcc, and to point a symbolic link to this location. For +example,</p> + +<blockquote> + <pre>% ln -s /usr/local/lib/lcc-4.1/sparc-solaris /usr/local/lib/lcc</pre> +</blockquote> + +<p>points <code>/usr/local/lib/lcc</code> to a build directory for lcc version 4.1 on the +SPARC under Solaris. Links into <code>/usr/local/lib</code> are created for the programs <code>lcc</code> +and <code>bprint</code>. Thus, a new distribution can be installed by building it in its +own build directory and changing one symbolic link to point to that directory. If these +conventions or their equivalents are followed, the host-specific parts of the driver +program, <code>lcc</code>, can be used unmodified.</p> + +<p>Installation on a UNIX system involves the following steps. Below, the build directory +is referred to as <code>BUILDDIR</code>. + +<ol> + <li>Create the build directory, using a version- and platform-specific naming convention as + suggested above, and record the name of this directory in the <code>BUILDDIR</code> + environment variable:<blockquote> + <pre>% setenv BUILDDIR /usr/local/lib/lcc-4.1/sparc-solaris +% mkdir -p $BUILDDIR</pre> + </blockquote> + <p>Here and below, commands assume the C shell. Also, you'll need a version of <code>mkdir</code> + that supports the <code>-p</code> option, which creates intermediate directories as + necessary.</p> + </li> + <li>Copy the man pages to the repository for local man pages, e.g.,<blockquote> + <pre>% cp doc/*.1 /usr/local/man/man1</pre> + </blockquote> + <p>Some users copy the man pages to the build directory and create the appropriate + symbolic links, e.g., </p> + <blockquote> + <pre>% cp doc/*.1 $BUILDDIR +% ln -s $BUILDDIR/*.1 /usr/local/man/man1</pre> + </blockquote> + </li> + <li>Platform-specific include files are in directories named <code>include/</code><em>target</em><code>/</code><em>os</em>. + Create the include directory in the build directory, and copy the include hierarchy for + your platform to this directory, e.g.,<blockquote> + <pre>% mkdir $BUILDDIR/include +% cp -p -R include/sparc/solaris/* $BUILDDIR/include</pre> + </blockquote> + <p>Again, some users create a symbolic link to the appropriate directory in the + distribution instead of copying the include files. For example, at Princeton, the + distributions are stored under <code>/proj/pkg/lcc</code>, so the included files are + "installed" by creating one symbolic link: </p> + <blockquote> + <pre>% ln -s /proj/pkg/lcc/4.1/include/sparc/solaris $BUILDDIR/include</pre> + </blockquote> + <p>If you're installing lcc on Linux, you <em>must</em> also plant a symbolic link named <code>gcc</code> + to gcc's library directory, because lcc uses gcc's C preprocessor and most of gcc's header + files:</p> + <blockquote> + <pre>% ln -s /usr/lib/gcc-lib/i486-linux/2.7.2.2 $BUILDDIR/gcc</pre> + </blockquote> + <p>The library directory shown above may be different on your Linux machine; to determine + the correct directory, browse <code>/usr/lib/gcc-lib</code>, or execute</p> + <blockquote> + <pre>% cc -v tst/8q.c</pre> + </blockquote> + <p>and examine the diagnostic output. Make sure that <code>$BUILDDIR/gcc/cpp</code> and <code>$BUILDDIR/gcc/include</code> + point to, respectively, gcc's C preprocessor and header files. On Linux, lcc looks for + include files in <code>$BUILDDIR/include</code>, <code>$BUILDDIR/gcc/include</code>, and <code>/usr/include</code>, + in that order; see <a HREF="#driver"><em>Building the Driver</em></a> and <a + href="../etc/linux.c"><code>etc/linux.c</code></a> for details.</p> + </li> + <li>The <a HREF="../makefile"><code>makefile</code></a> includes the file named by the <code>CUSTOM</code> + macro; the default is <code>custom.mk</code>, and an empty <code>custom.mk</code> is + included in the distribution. If desired, prepare a site-specification customization file + and define <code>CUSTOM</code> to the path of that file when invoking make in steps 5 and + 6, e.g.,<blockquote> + <pre>make CUSTOM=/users/drh/solaris.mk</pre> + </blockquote> + <p>You can, for example, use customization files to record site-specific values for macros + instead of using environment variables, and to record targets for the steps in this list.</p> + </li> + <li>Build the host-specific driver, creating a custom host-specific part, if necessary. See <a + HREF="#driver"><em>Building the Driver</em></a>.</li> + <li>Build the preprocessor, compiler proper, library, and other accessories. See <a + HREF="#rcc"><em>Building the Compiler</em></a>.</li> + <li>Plant symbolic links to the build directory and to the installed programs, e.g.,<blockquote> + <pre>% ln -s $BUILDDIR /usr/local/lib/lcc +% ln -s /usr/local/lib/{lcc,bprint} /usr/local/bin</pre> + </blockquote> + <p>Some users copy <code>bprint</code> and <code>lcc</code> into <code>/usr/local/bin</code> + instead of creating symbolic links. The advantange of creating the links for <code>lcc</code> + and <code>bprint</code> as shown is that, once established, they point indirectly to + whatever <code>/usr/local/lib/lcc</code> points to; installing a new version of lcc, say, + 4.2, can be done by changing <code>/usr/local/lib/lcc</code> to point to the 4.2 build + directory.</p> + </li> +</ol> + +<h2><a NAME="driver">Building the Driver</a></h2> + +<p>The preprocessor, compiler, assembler, and loader are invoked by a driver program, <code>lcc</code>, +which is similar to <code>cc</code> on most systems. It's described in the man page <code>doc/lcc.1</code>. +The driver is built by combining the host-independent part, <a href="../etc/lcc.c"><code>etc/lcc.c</code></a>, +with a small host-specific part. Distributed host-specific parts are named <code>etc/</code><em>os</em><code>.c</code>, +where <em>os</em> is the name of the operating system for the host on which <code>lcc</code> +is being installed. If you're following the installations conventions described above, you +can probably use one of the host-specific parts unmodified; otherwise, pick one that is +closely related to your platform, copy it to <em>whatever</em><code>.c</code>, and edit it +as described below. You should not have to edit <code>etc/lcc.c</code>.</p> + +<p>We'll use <a HREF="../etc/solaris.c"><code>etc/solaris.c</code></a> as an example in +describing how the host-specific part works. This example illustrates all the important +features. Make sure you have the environment variable <code>BUILDDIR</code> set correctly, +and build the driver with a <code>make</code> command, e.g.,</p> + +<blockquote> + <pre>% make HOSTFILE=etc/solaris.c lcc +cc -g -c -DTEMPDIR=\"/tmp\" -o /usr/local/lib/lcc-4.1/sparc-solaris/lcc.o etc/lcc.c +cc -g -c -o /usr/local/lib/lcc-4.1/sparc-solaris/host.o etc/solaris.c +cc -g -o /usr/local/lib/lcc-4.1/sparc-solaris/lcc /usr/local/lib/lcc-4.1/sparc-solaris/lcc.o /usr/local/lib/lcc-4.1/sparc-solaris/host.o</pre> +</blockquote> + +<p>The symbolic name <code>HOSTFILE</code> specifies the path to the host-specific part, +either one in the distribution or <em>whatever</em><code>.c</code>. Some versions of make +may require the <code>-e</code> option in order to read the environment.</p> + +<p>Here's <code>etc/solaris.c</code>:</p> + +<blockquote> + <pre>/* Sparcs running Solaris 2.5.1 at CS Dept., Princeton University */ + +#include <string.h> + +static char rcsid[] = "$ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $"; + +#ifndef LCCDIR +#define LCCDIR "/usr/local/lib/lcc/" +#endif +#ifndef SUNDIR +#define SUNDIR "/opt/SUNWspro/SC4.2/lib/" +#endif + +char *suffixes[] = { ".c", ".i", ".s", ".o", ".out", 0 }; +char inputs[256] = ""; +char *cpp[] = { LCCDIR "cpp", + "-D__STDC__=1", "-Dsparc", "-D__sparc__", "-Dsun", "-D__sun__", "-Dunix", + "$1", "$2", "$3", 0 }; +char *include[] = { "-I" LCCDIR "include", "-I/usr/local/include", + "-I/usr/include", 0 }; +char *com[] = { LCCDIR "rcc", "-target=sparc/solaris", + "$1", "$2", "$3", 0 }; +char *as[] = { "/usr/ccs/bin/as", "-Qy", "-s", "-o", "$3", "$1", "$2", 0 }; +char *ld[] = { "/usr/ccs/bin/ld", "-o", "$3", "$1", + SUNDIR "crti.o", SUNDIR "crt1.o", + SUNDIR "values-xa.o", "$2", "", + "-Y", "P," SUNDIR ":/usr/ccs/lib:/usr/lib", "-Qy", + "-L" LCCDIR, "-llcc", "-lm", "-lc", SUNDIR "crtn.o", 0 }; + +extern char *concat(char *, char *); + +int option(char *arg) { + if (strncmp(arg, "-lccdir=", 8) == 0) { + cpp[0] = concat(&arg[8], "/cpp"); + include[0] = concat("-I", concat(&arg[8], "/include")); + ld[12] = concat("-L", &arg[8]); + com[0] = concat(&arg[8], "/rcc"); + } else if (strcmp(arg, "-p") == 0) { + ld[5] = SUNDIR "mcrt1.o"; + ld[10] = "P," SUNDIR "libp:/usr/ccs/lib/libp:/usr/lib/libp:" + SUNDIR ":/usr/ccs/lib:/usr/lib"; + } else if (strcmp(arg, "-b") == 0) + ; + else if (strncmp(arg, "-ld=", 4) == 0) + ld[0] = &arg[4]; + else + return 0; + return 1; +}</pre> +</blockquote> + +<p><code>LCCDIR</code> defaults to <code>"/usr/local/lib/lcc/"</code> unless +it's defined by a <code>-D</code> option as part of <code>CFLAGS</code> in the make +command, e.g.,</p> + +<blockquote> + <pre>% make HOSTFILE=etc/solaris.c CFLAGS='-DLCCDIR=\"/v/lib/lcc/\"' lcc</pre> +</blockquote> + +<p>Note the trailing slash; <code>SUNDIR</code> is provided so you can use <code>etc/solaris.c</code> +even if you have a different version of the Sun Pro compiler suite. If you're using the +gcc compiler tools instead of the Sun Pro tools, see <a HREF="../etc/gcc-solaris.c"><code>etc/gcc-solaris.c</code></a>.</p> + +<p>Most of the host-specific code is platform-specific data and templates for the commands +that invoke the preprocessor, compiler, assembler, and loader. The <code>suffixes</code> +array lists the file name suffixes for C source files, preprocessed source files, assembly +language source files, object files, and executable files. <code>suffixes</code> must be +terminated with a null pointer, as shown above. The initialization of <code>suffixes</code> +in <code><a HREF="../etc/solaris.c">etc/solaris.c</a></code> are the typical ones for UNIX +systems. Each element of <code>suffixes</code> is actually a list of suffixes, separated +by semicolons; <code><a HREF="../etc/win32.c">etc/win32.c</a></code> holds an example:</p> + +<blockquote> + <pre>char *suffixes[] = { ".c;.C", ".i;.I", ".asm;.ASM;.s;.S", ".obj;.OBJ", ".exe", 0 };</pre> +</blockquote> + +<p>When a list is given, the first suffix is used whenever lcc needs to generate a file +name. For example, with <code><a HREF="../etc/win32.c">etc/win32.c</a></code>, lcc emits +the generated assembly code into <code>.asm</code> files.</p> + +<p>The <code>inputs</code> array holds a null-terminated string of directories separated +by colons or semicolons. These are used as the default value of <code>LCCINPUTS</code>, if +the environment variable <code>LCCINPUTS</code> is not set; see the <a HREF="lcc.pdf">man +page</a>.</p> + +<p>Each command template is an array of pointers to strings terminated with a null +pointer; the strings are full path names of commands, arguments, or argument placeholders, +which are described below. Commands are executed in a child process, and templates can +contain multiple commands by separating commands with newlines. The driver runs each +command in a new process.</p> + +<p>The <code>cpp</code> array gives the command for running lcc's preprocessor, <code>cpp</code>. +Literal arguments specified in templates, e.g., <code>"-Dsparc"</code> in the <code>cpp</code> +command above, are passed to the command as given.</p> + +<p>The strings <code>"$1"</code>, <code>"$2"</code>, and <code>"$3"</code> +in templates are placeholders for <em>lists</em> of arguments that are substituted in a +copy of the template before the command is executed. <code>$1</code> is replaced by the <em>options</em> +specified by the user; for the preprocessor, this list always contains at least <code>-D__LCC__</code>. +<code>$2</code> is replaced by the <em>input</em> files, and <code>$3</code> is replaced +by the <em>output</em> file.</p> + +<p>Zero-length arguments after replacement are removed from the argument list before the +command is invoked. So, for example, if the preprocessor is invoked without an output +file, <code>"$3"</code> becomes <code>""</code>, which is removed from +the final argument list.</p> + +<p>The <code>include</code> array is a list of <code>-I</code> options that specify which +directives should be searched to satisfy include directives. These directories are +searched in the order given. The first directory should be the one to which the ANSI +header files were copied as described in <a HREF="#unix">UNIX</a> or <a HREF="#win32">Windows</a> +installation instructions. The driver adds these options to <code>cpp</code>'s arguments +when it invokes the preprocessor, except when <code>-N</code> is specified.</p> + +<p><code>com</code> gives the command for invoking the compiler. This template can appear +as shown above in a custom host-specific part, but the option <code>-target=sparc/solaris</code> +should be edited to the <em>target</em><code>/</code><em>os</em> for your platform. If <code>com[1]</code> +includes the string "<code>win32</code>", the driver assumes it's running on +Windows. lcc can generate code for <em>all</em> of the <em>target</em><code>/</code><em>os</em> +combinations listed in the file <code>src/bind.c</code>. The <code>-target</code> option +specifies the default combination. The driver's <code>-Wf</code> option can be used to +specify other combinations; the <a HREF="lcc.pdf">man page</a> elaborates.</p> + +<p><code>as</code> gives the command for invoking the assembler. On Linux, you must be +running at least version 2.8.1 of the GNU assembler; earlier versions mis-assemble some +instructions emitted by lcc.</p> + +<p><code>ld</code> gives the command for invoking the loader. For the other commands, the +list <code>$2</code> contains a single file; for <code>ld</code>, <code>$2</code> contains +all ".o" files and libraries, and <code>$3</code> is <code>a.out</code>, unless +the <code>-o</code> option is specified. As suggested in the code above, <code>ld</code> +must also specify the appropriate startup code and default libraries, including the lcc +library, <code>liblcc.a</code>.</p> + +<p>The <code>option</code> function is described below; the minimal <code>option</code> +function just returns 0.</p> + +<p>You can test <code>lcc</code> with the options <code>-v -v</code> to display the +commands that would be executed, e.g.,</p> + +<blockquote> + <pre>% $BUILDDIR/lcc -v -v foo.c baz.c mylib.a -lX11 +/usr/local/lib/lcc-4.1/lcc $ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $ +foo.c: +/usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__ -I/usr/local/lib/lcc/include -I/usr/local/include -I/usr/include foo.c /tmp/lcc266290.i +/usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc266290.i /tmp/lcc266291. +s +/usr/ccs/bin/as -Qy -s -o /tmp/lcc266292.o /tmp/lcc266291.s +baz.c: +/usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__ -I/usr/local/lib/lcc/include -I/usr/local/include -I/usr/include baz.c /tmp/lcc266290.i +/usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc266290.i /tmp/lcc266291.s +/usr/ccs/bin/as -Qy -s -o /tmp/lcc266293.o /tmp/lcc266291.s +/usr/ccs/bin/ld -o a.out /opt/SUNWspro/SC4.2/lib/crti.o /opt/SUNWspro/SC4.2/lib/crt1.o /opt/SUNWspro/SC4.2/lib/values-xa.o /tmp/lcc266292.o /tmp/lcc266293.o mylib.a -lX11 -Y P,/opt/SUNWspro/SC4.2/lib/:/usr/ccs/lib:/usr/lib -Qy -L/usr/local/lib/lcc/ -llcc -lm -lc /opt/SUNWspro/SC4.2/lib/crtn.o +rm /tmp/lcc266293.o /tmp/lcc266290.i /tmp/lcc266291.s /tmp/lcc266292.o</pre> +</blockquote> + +<p>As the output shows, <code>lcc</code> places temporary files in <code>/tmp</code>; if +any of the environment variables <code>TMP</code>, <code>TEMP</code>, and <code>TMPDIR</code> +are set, they override this default (in the order shown) as does the <code>-tempdir=</code><em>dir</em> +option. The default can be changed by defining <code>TEMPDIR</code> in <code>CFLAGS</code> +when building the driver.</p> + +<p>The <code>option</code> function is called for the options <code>-Wo</code>, <code>-g</code>, +<code>-p</code>, <code>-pg</code>, and <code>-b</code> because these compiler options +might also affect the loader's arguments. For these options, the driver calls <code>option(arg)</code> +to give the host-specific code an opportunity to edit the <code>ld</code> command, if +necessary. <code>option</code> can change <code>ld</code>, if necessary, and return 1 to +announce its acceptance of the option. If the option is unsupported, <code>option</code> +should return 0.</p> + +<p>For example, in response to <code>-g</code>, the <code>option</code> function shown +above accepts the option but does nothing else, because the <code>ld</code> and <code>as</code> +commands don't need to be modified on the SPARC. <code>-g</code> will also be added to the +compiler's options by the host-independent part of the driver. The <code>-p</code> causes <code>option</code> +to change the name of the startup code and changed the list of libraries. The <code>-b</code> +option turns on <code>lcc</code>'s per-expression profiling, the code for which is in <code>liblcc.a</code>, +so <code>option</code> need no nothing.</p> + +<p>On SPARCs, the driver also recognizes <code>-Bstatic</code> and <code>-Bdynamic</code> +as linker options. The driver recognizes but ignores "<code>-target</code> <em>name</em>" +option.</p> + +<p>The option <code>-Wo</code><em>arg</em> causes the driver to pass <em>arg</em> to <code>option</code>. +Such options have no other effect; this mechanism is provided to support system-specific +options that affect the commands executed by the driver. As illustrated above, +host-specific parts should support the <code>-Wo-lccdir=</code><em>dir</em> option, which +causes lcc's compilation components to be found in <em>dir</em>, because this option is +used by the test scripts, and because the driver simulates a <code>-Wo-lccdir</code> +option with the value of the environment variable <code>LCCDIR</code>, if it's defined. +The code above rebuilds the paths to the include files, preprocessor, compiler, and +library by calling <code>concat</code>, which is defined in <code>etc/lcc.c</code>.</p> + +<h2><a NAME="rcc">Building the Compiler and Accessories</a></h2> + +<p>To build the rest of compilation components make sure <code>BUILDDIR</code> is set +appropriately and type "<code>make all</code>". This command builds <code>librcc.a</code> +(the compiler's private library), <code>rcc</code> (the compiler proper), <code>lburg</code> +(the code-generator generator), <code>cpp</code> (the preprocessor), <code>liblcc.a</code> +(the runtime library), and <code>bprint</code> (the profile printer), all in <code>BUILDDIR</code>. +There may be warnings, but there should be no errors. If you're using an ANSI/ISO compiler +other than <code>cc</code>, specify its name with the <code>CC=</code> option, e.g., +"<code>make CC=gcc all</code>". If you're running on a DEC ALPHA, use "<code>make +CC='cc -std1' all</code>"; the <code>-std1</code> option is essential on the ALPHA. +If you're on a DEC 5000 running Ultrix 4.3, use "<code>make CC=c89 all</code>".</p> + +<p>Once <code>rcc</code> is built with the host C compiler, run the test suite to verify +that <code>rcc</code> is working correctly. If any of the steps below fail, contact us +(see <a HREF="#bugs"><em>Reporting Bugs</em></a>). The commands in the makefile run the +shell script <code>src/run.sh</code> on each C program in the test suite, <code>tst/*.c</code>. +It uses the driver, <code>$BUILDDIR/lcc</code>, so you must have the driver in the build +directory before testing <code>rcc</code>. The <em>target</em><code>/</code><em>os</em> +combination is read from the variable <code>TARGET</code>, which must be specified when +invoking <code>make</code>:</p> + +<blockquote> + <pre>% make TARGET=sparc/solaris test +mkdir -p /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/8q.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/array.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cf.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cq.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cvt.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/fields.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/front.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/incr.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/init.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/limits.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/paranoia.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/sort.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/spill.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/stdarg.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/struct.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/switch.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/wf1.s: +/usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/yacc.s:</pre> +</blockquote> + +<p>Each line in the output above is of the form</p> + +<blockquote> + <p><code>$BUILDDIR/rcc -target=</code><em>target</em><code>/</code><em>os</em><code>$BUILDDIR/</code><em>target</em><code>/</code><em>os</em><code>/</code><em>X</em><code>.s:</code></p> +</blockquote> + +<p>where <em>X</em> is the base name of the C program <em>X</em><code>.c</code> in the +test suite. This output identifies the compiler and the target, e.g., "<code>$BUILDDIR/rcc</code> +is generating code for a <code>sparc</code> running the <code>solaris</code> operating +system."</p> + +<p>For each program in the test suite, <code>src/run.sh</code> compiles the program, drops +the generated assembly language code in <code>BUILDDIR</code>/<em>target</em><code>/</code><em>os</em>, +and uses <code>diff</code> to compare the generated assembly code with the expected code +(the code expected for <code>tst/8q.c</code> on the SPARC under Solaris is in <code>sparc/solaris/tst/8q.sbk</code>, +etc.). If there are differences, the script executes the generated code with the input +given in <code>tst</code> (the input for <code>tst/8q.c</code> is in <code>tst/8q.0</code>, +etc.) and compares the output with the expected output (the expected output from <code>tst/8q.c</code> +on the SPARC under Solaris is in <code>sparc/solaris/tst/8q.1bk</code>, etc.). The script +also compares the diagnostics from the compiler with the expected diagnostics.</p> + +<p>On some systems, there may be a few differences between the generated code and the +expected code. These differences occur because the expected code is generated by cross +compilation and the least significant bits of some floating-point constants differ from +those bits in constants generated on your system. On Linux, there may be differences +because of differences in the header files between our system and yours. There should be +no differences in the output from executing the test programs.</p> + +<p>Next, run the "triple test", which builds <code>rcc</code> using itself:</p> + +<blockquote> + <pre>% make triple +/usr/local/lib/lcc-4.1/sparc-solaris/lcc -o /usr/local/lib/lcc-4.1/sparc-solaris/1rcc -d0.6 -Wo-lccdir=/usr/local/lib/lcc-4.1/sparc-solaris -B/usr/local/lib/lcc-4.1/sparc-solaris/ -Isrc src/*.c +src/alloc.c: +... +src/x86.c: +/usr/local/lib/lcc-4.1/sparc-solaris/lcc -o /usr/local/lib/lcc-4.1/sparc-solaris/1rcc -d0.6 -Wo-lccdir=/usr/local/lib/lcc-4.1/sparc-solaris -B/usr/local/lib/lcc-4.1/sparc-solaris/ -Isrc src/*.c +src/alloc.c: +... +src/x86.c: +strip /usr/local/lib/lcc-4.1/sparc-solaris/[12]rcc +dd if=/usr/local/lib/lcc-4.1/sparc-solaris/1rcc of=/usr/local/lib/lcc-4.1/sparc-solaris/rcc1 bs=512 skip=1 +769+1 records in +769+1 records out +dd if=/usr/local/lib/lcc-4.1/sparc-solaris/2rcc of=/usr/local/lib/lcc-4.1/sparc-solaris/rcc2 bs=512 skip=1 +769+1 records in +769+1 records out +if cmp /usr/local/lib/lcc-4.1/sparc-solaris/rcc[12]; then \ + mv /usr/local/lib/lcc-4.1/sparc-solaris/2rcc /usr/local/lib/lcc-4.1/sparc-solaris/rcc; \ + rm -f /usr/local/lib/lcc-4.1/sparc-solaris/1rcc /usr/local/lib/lcc-4.1/sparc-solaris/rcc[12]; fi</pre> +</blockquote> + +<p>This command builds <code>rcc</code> twice; once using the <code>rcc</code> built by <code>cc</code> +and again using the <code>rcc</code> built by <code>lcc</code>. The resulting binaries are +compared. They should be identical, as shown at the end of the output above. If they +aren't, our compiler is generating incorrect code; <a HREF="#bugs">contact</a> us.</p> + +<p>The final version of <code>rcc</code> should also pass the test suite; that is, the +output from</p> + +<blockquote> + <pre>% make TARGET=sparc/solaris test</pre> +</blockquote> + +<p>should be identical to that from the previous <code>make test</code>.</p> + +<p>The command "<code>make clean</code>" cleans up, but does not remove <code>rcc</code>, +etc., and "<code>make clobber</code>" cleans up and removes <code>lcc</code>, <code>rcc</code>, +and the other accessories. Test directories under <code>BUILDDIR</code> are <em>not</em> +removed; you'll need to remove these by hand, e.g.,</p> + +<blockquote> + <pre>% rm -fr $BUILDDIR/sparc</pre> +</blockquote> + +<p>The code generators for the other targets can be tested by specifying the desired <em>target</em><code>/</code><em>os</em> +and setting an environment variable that controls what <code>src/run.sh</code> does. For +example, to test the MIPS code generator, type</p> + +<blockquote> + <pre>% setenv REMOTEHOST noexecute +% make TARGET=mips/irix test</pre> +</blockquote> + +<p>As above, <code>src/run.sh</code> compares the MIPS code generated with what's +expected. There should be no differences. Setting <code>REMOTEHOST</code> to <code>noexecute</code> +suppresses the assembly and execution of the generated code. If you set <code>REMOTEHOST</code> +to the name of a MIPS machine to which you can <code>rlogin</code>, <code>src/run.sh</code> +will <code>rcp</code> the generated code to that machine and execute it there, if +necessary. See <code>src/run.sh</code> for the details.</p> + +<p>You can use lcc as a cross compiler. The options <code>-S</code> and <code>-Wf-target=</code><em>target/os</em> +generate assembly code for the specified target, which is any of those listed in the file <code>src/bind.c</code>. +For example, </p> + +<blockquote> + <pre>% lcc -Wf-target=mips/irix -S tst/8q.c</pre> +</blockquote> + +<p>generates MIPS code for <code>tst/8q.c</code> in <code>8q.s</code>.</p> + +<p>lcc can also generate code for a "symbolic" target. This target is used +routinely in front-end development, and its output is a printable representation of the +input program, e.g., the dags constructed by the front end are printed, and other +interface functions print their arguments. You can specify this target with the option <code>-Wf-target=symbolic</code>. +For example,</p> + +<blockquote> + <pre>% lcc -Wf-target=symbolic -S tst/8q.c</pre> +</blockquote> + +<p>generates symbolic output for <code>tst/8q.c</code> in <code>8q.s</code>. Adding <code>-Wf-html</code> +causes the symbolic target to emit HTML instead of plain text. Finally, the option <code>-Wf-target=null</code> +specifies the "null" target for which lcc emits nothing and thus only checks the +syntax and semantics of its input files.</p> + +<h2><a NAME="win32">Installation on Windows NT 4.0 or Windows 95/98</a></h2> + +<p>On Windows NT 4.0 and Windows 95/98, lcc is designed to work with Microsoft's Visual +C++ 5.0 (VC) and Microsoft's Assembler, MASM 6.11d. It uses the VC header files, +libraries, and command-line tools, and it uses MASM to assemble the code it generates. If +you have MASM 6.11, make sure you <a +HREF="http://support.microsoft.com/support/kb/articles/Q138/9/83.asp">upgrade to 6.11d</a>, +because earlier 6.11 releases do not generate correct COFF object files.</p> + +<p>Building the distribution components from the ground up requires Microsoft's Visual +C/C++ 5.0 compiler, Microsoft's make, <code>nmake</code>, and the standard Windows command +interpreter. <a HREF="../makefile.nt"><code>makefile.nt</code></a> is written to use only <code>nmake</code>. +As on UNIX systems, the compilation components are installed in a single <em>build +directory</em>, and the top-level programs, <code>lcc.exe</code> and <code>bprint.exe</code>, +are installed in a directory on the PATH. If the conventions used below are followed, the +Windows-specific parts of the driver program, <code>lcc.exe</code>, can be used +unmodified.</p> + +<p>Building from the source distribution on a Windows system involves the following steps. +Below, the build directory is referred to as <code>BUILDDIR</code>, and the distribution +is in <code>\dist\lcc\4.1</code>. + +<ol> + <li>Create the build directory, perhaps using a version- and platform-specific naming + convention as suggested in <a HREF="#unix"><em>Installation on UNIX</em></a>, and record + the name of this directory in the <code>BUILDDIR</code> environment variable:<blockquote> + <pre>C:\dist\lcc\4.1>set BUILDDIR=\progra~1\lcc\4.1\bin +C:\dist\lcc\4.1>mkdir %BUILDDIR%</pre> + </blockquote> + <p>The default build, or installation, directory is <code>\Program Files\lcc\4.1\bin</code>, + but the <code>nmake</code> commands require that you use the corresponding 8.3 file name, <code>progra~1</code>, + instead of <code>Program Files</code>.</p> + </li> + <li><a HREF="../etc/win32.c"><code>etc\win32.c</code></a> is the Windows-specific part of + the driver. It assumes that environment variable <code>include</code> gives the locations + of the VC header files and that the linker (<code>link.exe</code>) and the assembler (<code>ml.exe</code>) + are on the PATH. It also assumes that the macro <code>LCCDIR</code> gives the build + directory. If necessary, revise a copy of <a HREF="../etc/win32.c"><code>etc\win32.c</code></a> + to reflect the conventions on your computer (see <a HREF="#driver"><em>Building the Driver</em></a>), + then build the driver, specifying the default temporary directory, if necessary:<blockquote> + <pre>C:\dist\lcc\4.1>nmake -f makefile.nt TEMPDIR=\\temp HOSTFILE=etc/win32.c lcc +... + cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -c -DTEMPDIR=\"\\temp\" -Fo\progra~1\lcc\4.1\bin\lcc.obj etc/lcc.c +lcc.c + cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -c -Fo\progra~1\lcc\4.1\bin\host.obj etc/win32.c +win32.c + cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -Fe\progra~1\lcc\4.1\bin\lcc.exe \progra~1\lcc\4.1\bin\lcc.obj \progra~1\lcc\4.1\bin\host.obj</pre> + </blockquote> + <p>If you make a copy of <code>etc\win32.c</code>, specify the path of the copy as the + value of <code>HOSTFILE</code>. For example, if you copy <code>etc\win32.c</code> to <code>BUILDDIR</code> + and edit it, use the command</p> + <blockquote> + <pre>C:\dist\lcc\4.1>nmake -f makefile.nt TEMPDIR=\\temp HOSTFILE=%BUILDDIR%\win32.c lcc</pre> + </blockquote> + </li> + <li>Build the preprocessor, compiler proper, library, and other accessories (see <a + HREF="#rcc"><em>Building the Compiler</em></a>):<blockquote> + <pre>C:\dist\lcc\4.1>nmake -f makefile.nt all</pre> + </blockquote> + <p>This command uses the VC command-line tools <code>cl</code> and <code>lib</code> to + build <code>bprint.exe</code>, <code>cpp.exe</code>, <code>lburg.exe</code>, <code>liblcc.lib</code>, + <code>librcc.lib</code>, and <code>rcc.exe</code>, all in <code>BUILDDIR</code>. There may + be some warnings, but there should be no warnings.</p> + </li> + <li>Create a test directory and run the test suite:<blockquote> + <pre>C:\dist\lcc\4.1>mkdir %BUILDDIR%\x86\win32\tst +C:\dist\lcc\4.1>nmake -f makefile.nt test</pre> + </blockquote> + <p>This command compiles each program in <a HREF="../tst">tst</a>, compares the generated + assembly code and diagnostics with the expected assembly code and diagnostics, executes + the program, and compares the output with the expected output (using <code>fc</code>). For + example, when the nmake command compiles <a HREF="../tst/8q.c"><code>tst\8q.c</code></a>, + it leaves the generated assembly code and diagnostic output in <code>%BUILDDIR%\x86\win32\tst\8q.s</code> + and <code>%BUILDDIR%\x86\win32\tst\8q.2</code>, and it compares them with the expected + results in <code>x86\win32\tst\8q.sbk</code>. It builds the executable program in <code>%BUILDDIR%\x86\win32\tst\8q.exe</code>, + runs it, and redirects the output to <code>%BUILDDIR%\x86\win32\tst\8q.1</code>, which it + compares with <code>x86\win32\tst\8q.1bk</code>. The output from this step is voluminous, + but there should be no differences and no errors.</p> + </li> + <li>Run the "triple" test, which compiles <code>rcc</code> with itself and + verifies the results:<blockquote> + <pre>C:\dist\lcc\4.1>nmake -f makefile.nt triple +... +\progra~1\lcc\4.1\bin\x86.c: + Assembling: C:/TEMP/lcc2001.asm + fc /b \progra~1\lcc\4.1\bin\1rcc.exe \progra~1\lcc\4.1\bin\2rcc.exe +Comparing files \progra~1\lcc\4.1\bin\1rcc.exe and \progra~1\lcc\4.1\bin\2RCC.EXE +00000088: B4 D5</pre> + </blockquote> + <p>This command builds <code>rcc</code> twice; once using the <code>rcc</code> built by VC + and again using the <code>rcc</code> built by <code>lcc</code>. The resulting binaries are + compared using <code>fc</code>. They should be identical, except for one or two bytes of + timestamp data, as shown at the end of the output above. If they aren't, our compiler is + generating incorrect code; <a HREF="#bugs">contact</a> us.</p> + </li> + <li>Copy <code>lcc.exe</code> and <code>bprint.exe</code> to a directory on your PATH, e.g.,<blockquote> + <pre>C:\dist\lcc\4.1>copy %BUILDDIR%\lcc.exe \bin + 1 file(s) copied. + +C:\dist\lcc\4.1>copy %BUILDDIR%\bprint.exe \bin + 1 file(s) copied.</pre> + </blockquote> + </li> + <li>Finally, clean up:<blockquote> + <pre>C:\dist\lcc\4.1>nmake -f makefile.nt clean</pre> + </blockquote> + <p>This command removes the derived files in <code>BUILDDIR</code>, but does not remove <code>rcc.exe</code>, + etc.; "<code>nmake -f makefile.nt clobber</code>" cleans up and removes all + executables and libraries. Test directories under <code>BUILDDIR</code> are <em>not</em> + removed; you'll need to remove these by hand, e.g.,</p> + <blockquote> + <pre>C:\dist\lcc\4.1>rmdir %BUILDDIR%\x86 /s +\progra~1\lcc\4.1\bin\x86, Are you sure (Y/N)? y</pre> + </blockquote> + </li> +</ol> + +<h2><a NAME="bugs">Reporting Bugs</a></h2> + +<p>lcc is a large, complex program. We find and repair errors routinely. If you think that +you've found a error, follow the steps below, which are adapted from the instructions in +Chapter 1 of <cite>A Retargetable C Compiler: Design and Implementation</cite>. + +<ol> + <li>If you don't have a source file that displays the error, create one. Most errors are + exposed when programmers try to compile a program they think is valid, so you probably + have a demonstration program already.</li> + <li>Preprocess the source file and capture the preprocessor output. Discard the original + code.</li> + <li>Prune your source code until it can be pruned no more without sending the error into + hiding. We prune most error demonstrations to fewer than five lines.</li> + <li>Confirm that the source file displays the error with the <em>distributed</em> version of + lcc. If you've changed lcc and the error appears only in your version, then you'll have to + chase the error yourself, even if it turns out to be our fault, because we can't work on + your code.</li> + <li>Annotate your code with comments that explain why you think that lcc is wrong. If lcc + dies with an assertion failure, please tell us where it died. If lcc crashes, please + report the last part of the call chain if you can. If lcc is rejecting a program you think + is valid, please tell us why you think it's valid, and include supporting page numbers in + the ANSI Standard, Appendix A in <cite>The C Programming Language</cite>, or the + appropriate section in <cite>C: A Reference Manual</cite>, 4th edition by S. B. Harbison + and G. L. Steele, Jr. (Prentice Hall, 1995). If lcc silently generates incorrect code for + some construct, please include the corrupt assembly code in the comments and flag the + incorrect instructions if you can.</li> + <li>Confirm that your error hasn't been fixed already. The latest version of lcc is always + available for anonymous <code>ftp</code> from <code>ftp.cs.princeton.edu</code> in <a + HREF="ftp://ftp.cs.princeton.edu/pub/lcc"><code>pub/lcc</code></a>. A <a + HREF="ftp://ftp.cs.princeton.edu/pub/lcc/README"><code>README</code></a> file there gives + acquistion details, and the <a HREF="../LOG"><code>LOG</code></a> file reports what errors + were fixed and when they were fixed. If you report a error that's been fixed, you might + get a canned reply.</li> + <li>Send your program by electronic mail to <code>lcc-bugs@cs.princeton.edu</code>. Please + send only valid C programs; put all remarks in C comments so that we can process reports + semiautomatically.</li> +</ol> + +<h2><a NAME="mailinglist">Keeping in Touch</a></h2> + +<p>There is an lcc mailing list for general information about lcc. To be added to the +list, send a message with the 1-line body</p> + +<blockquote> + <pre>subscribe lcc</pre> +</blockquote> + +<p>to <code>majordomo@cs.princeton.edu</code>. This line must appear in the message body; +"Subject:" lines are ignored. To learn more about mailing lists served by <code>majordomo</code>, +send a message with the 1-word body "<code>help</code>" to <code>majordomo@cs.princeton.edu</code>. +Mail sent to <code>lcc@cs.princeton.edu</code> is forwarded to everyone on the mailing +list.</p> + +<p>There is also an <code>lcc-bugs</code> mailing list for reporting bugs; subscribe to it +by sending a message with the 1-line body </p> + +<blockquote> + <pre>subscribe lcc-bugs</pre> +</blockquote> + +<p>to <code>majordomo@cs.princeton.edu</code>. Mail addressed to <var>lcc-bugs@cs.princeton.edu</var> +is forwarded to everyone on this list.</p> + +<hr> + +<address> + <a HREF="http://www.research.microsoft.com/~cwfraser/">Chris Fraser</a> / <a + HREF="mailto:cwfraser@microsoft.com">cwfraser@microsoft.com</a><br> + <a HREF="http://www.research.microsoft.com/~drh/">David Hanson</a> / <a + HREF="mailto:drh@microsoft.com">drh@microsoft.com</a><br> + $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ +</address> +</body> +</html> diff --git a/src/tools/lcc/doc/lcc.1 b/src/tools/lcc/doc/lcc.1 new file mode 100644 index 0000000..fe91bca --- /dev/null +++ b/src/tools/lcc/doc/lcc.1 @@ -0,0 +1,605 @@ +.\" $Id: lcc.1 145 2001-10-17 21:53:10Z timo $ +.TH LCC 1 "local \- $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $" +.SH NAME +lcc \- ANSI C compiler +.SH SYNOPSIS +.B lcc +[ +.I option +| +.I file +]... +.br +.SH DESCRIPTION +.PP +.I lcc +is an ANSI C compiler for a variety of platforms. +.PP +Arguments whose names end with `.c' (plus `.C' under Windows) are taken to be +C source programs; they are preprocessed, compiled, and +each object program is left on the file +whose name is that of the source with `.o' (UNIX) or `.obj' (Windows) +substituted for the extension. +Arguments whose names end with `.i' are treated similarly, +except they are not preprocessed. +In the same way, +arguments ending with `.s' (plus `.S', `.asm', and `.ASM', under Windows) +are taken to be assembly source programs +and are assembled, producing an object file. +If there are no arguments, +.I lcc +summarizes its options on the standard error. +.PP +.I lcc +deletes an object file if and only if exactly one +source file is mentioned and no other file +(source, object, library) or +.B \-l +option is mentioned. +.PP +If the environment variable +.B LCCINPUTS +is set, +.I lcc +assumes it gives a semicolon- or colon-separated list of directories in which to +look for source and object files whose names do not begin with `/'. +These directories are also added to the list of directories +searched for libraries. +If +.B LCCINPUTS +is defined, it must contain `.' in order for the current directory +to be searched for input files. +.PP +.I lcc +uses ANSI standard header files (see `FILES' below). +Include files not found in the ANSI header files +are taken from the normal default include areas, +which usually includes +.BR /usr/include . +Under Windows, if the environment variable +.B include +is defined, it gives a semicolon-separated list of directories in which to search for +header files. +.PP +.I lcc +interprets the following options; unrecognized options are +taken as loader options (see +.IR ld (1)) +unless +.BR \-c , +.BR \-S , +or +.B \-E +precedes them. +Except for +.BR \-l , +all options are processed before any of the files +and apply to all of the files. +Applicable options are passed to each compilation phase in the order given. +.TP +.B \-c +Suppress the loading phase of the compilation, and force +an object file to be produced even if only one program is compiled. +.TP +.B \-g +Produce additional symbol table information for the local debuggers. +.I lcc +warns when +.B \-g +is unsupported. +.TP +.BI \-Wf\-g n , x +Set the debugging level to +.I n +and emit source code as comments into the generated assembly code; +.I x +must be the assembly language comment character. +If +.I n +is omitted, it defaults to 1, which is similar to +.BR \-g . +Omitting +.BI , x +just sets the debugging level to +.IR n . +.TP +.B \-w +Suppress warning diagnostics, such as those +announcing unreferenced statics, locals, and parameters. +The line +.I +#pragma ref id +simulates a reference to the variable +.IR id . +.TP +.BI \-d n +Generate jump tables for switches whose density is at least +.IR n , +a floating point constant between zero and one. +The default is 0.5. +.TP +.B \-A +Warns about +declarations and casts of function types without prototypes, +assignments between pointers to ints and pointers to enums, and +conversions from pointers to smaller integral types. +A second +.B \-A +warns about +unrecognized control lines, +nonANSI language extensions and source characters in literals, +unreferenced variables and static functions, +declaring arrays of incomplete types, +and exceeding +.I some +ANSI environmental limits, like more than 257 cases in switches. +It also arranges for duplicate global definitions in separately compiled +files to cause loader errors. +.TP +.B \-P +Writes declarations for all defined globals on standard error. +Function declarations include prototypes; +editing this output can simplify conversion to ANSI C. +This output may not correspond to the input when +there are several typedefs for the same type. +.TP +.B \-n +Arrange for the compiler to produce code +that tests for dereferencing zero pointers. +The code reports the offending file and line number and calls +.IR abort (3). +.TP +.B \-O +is ignored. +.TP +.B \-S +Compile the named C programs, and leave the +assembler-language output on corresponding files suffixed `.s' or `.asm'. +.TP +.B \-E +Run only the preprocessor on the named C programs +and unsuffixed file arguments, +and send the result to the standard output. +.TP +.BI \-o " output" +Name the output file +.IR output . +If +.B \-c +or +.B \-S +is specified and there is exactly one source file, +this option names the object or assembly file, respectively. +Otherwise, this option names the final executable +file generated by the loader, and `a.out' (UNIX) or `a.exe' (Windows) is left undisturbed. +.I lcc +warns if +.B \-o +and +.B \-c +or +.B \-S +are given with more than one source file and ignores the +.B \-o +option. +.TP +.BI \-D name=def +Define the +.I name +to the preprocessor, as if by `#define'. +If +.I =def +is omitted, the name is defined as "1". +.TP +.BI \-U name +Remove any initial definition of +.IR name . +.TP +.BI \-I dir +`#include' files +whose names do not begin with `/' are always +sought first in the directory of the +.I file +arguments, then in directories named in +.B \-I +options, then in directories on a standard list. +.TP +.B \-N +Do not search +.I any +of the standard directories for `#include' files. +Only those directories specified by subsequent explicit +.B \-I +options will be searched, in the order given. +.TP +.BI \-B str +Use the compiler +.BI "" str rcc +instead of the default version. +Note that +.I str +often requires a trailing slash. +On Sparcs only, +.B \-Bstatic +and +.BI \-Bdynamic +are passed to the loader; see +.IR ld (1). +.TP +.BI \-Wo\-lccdir= dir +Find the preprocessor, compiler proper, and include directory +in the directory +.I dir/ +or +.I +dir\\. +If the environment variable +.B LCCDIR +is defined, it gives this directory. +.I lcc +warns when this option is unsupported. +.TP +.B \-Wf-unsigned_char=1 +.br +.ns +.TP +.B \-Wf-unsigned_char=0 +makes plain +.B char +an unsigned (1) or signed (0) type; by default, +.B char +is signed. +.TP +.B \-Wf\-wchar_t=unsigned_char +.br +.ns +.TP +.B \-Wf\-wchar_t=unsigned_short +.br +.ns +.TP +.B \-Wf\-wchar_t=unsigned_int +Makes wide characters the type indicated; by default, +wide characters are unsigned short ints, and +.B wchar_t +is a typedef for unsigned short defined in stddef.h. +The definition for +.B wchar_t +in stddef.h must correspond to the type specified. +.TP +.B \-v +Print commands as they are executed; some of the executed +programs are directed to print their version numbers. +More than one occurrence of +.B \-v +causes the commands to be printed, but +.I not +executed. +.TP +.BR \-help " or " \-? +Print a message on the standard error summarizing +.IR lcc 's +options and giving the values of the environment variables +.B LCCINPUTS +and +.BR LCCDIR , +if they are defined. +Under Windows, the values of +.B include +and +.B lib +are also given, if they are defined. +.TP +.B \-b +Produce code that counts the number of times each expression is executed. +If loading takes place, arrange for a +.B prof.out +file to be written when the object program terminates. +A listing annotated with execution counts can then be generated with +.IR bprint (1). +.I lcc +warns when +.B \-b +is unsupported. +.B \-Wf\-C +is similar, but counts only the number of function calls. +.TP +.B \-p +Produce code that counts the number of times each function is called. +If loading takes place, replace the standard startup +function by one that automatically calls +.IR monitor (3) +at the start and arranges to write a +.B mon.out +file when the object program terminates normally. +An execution profile can then be generated with +.IR prof (1). +.I lcc +warns when +.B \-p +is unsupported. +.TP +.B \-pg +Causes the compiler to produce counting code like +.BR \-p , +but invokes a run-time recording mechanism that keeps more +extensive statistics and produces a +.B gmon.out +file at normal termination. +Also, a profiling library is searched, in lieu of the standard C library. +An execution profile can then be generated with +.IR gprof (1). +.I lcc +warns when +.B \-pg +is unsupported. +.TP +.BI \-t name +.br +.ns +.TP +.BI \-t +Produce code to print the name of the function, an activation number, +and the name and value of each argument at function entry. +At function exit, produce code to print +the name of the function, the activation number, and the return value. +By default, +.I printf +does the printing; if +.I name +appears, it does. +For null +.I char* +values, "(null)" is printed. +.BI \-target +.I name +is accepted, but ignored. +.TP +.BI \-tempdir= dir +Store temporary files in the directory +.I dir/ +or +.I +dir\\. +The default is usually +.BR /tmp . +.TP +.BI \-W xarg +pass argument +.I arg +to the program indicated by +.IR x ; +.I x +can be one of +.BR p , +.BR f , +.BR a , +or +.BR l , +which refer, respectively, to the preprocessor, the compiler proper, +the assembler, and the loader. +.I arg +is passed as given; if a +.B \- +is expected, it must be given explicitly. +.BI \-Wo arg +specifies a system-specific option, +.IR arg . +.PP +Other arguments +are taken to be either loader option arguments, or C-compatible +object programs, typically produced by an earlier +.I lcc +run, or perhaps libraries of C-compatible routines. +Duplicate object files are ignored. +These programs, together with the results of any +compilations specified, are loaded (in the order +given) to produce an executable program with name +.BR a.out +(UNIX) or +.BR a.exe +(Windows). +.PP +.I lcc +assigns the most frequently referenced scalar parameters and +locals to registers whenever possible. +For each block, +explicit register declarations are obeyed first; +remaining registers are assigned to automatic locals if they +are `referenced' at least 3 times. +Each top-level occurrence of an identifier +counts as 1 reference. Occurrences in a loop, +either of the then/else arms of an if statement, or a case +in a switch statement each count, respectively, as 10, 1/2, or 1/10 references. +These values are adjusted accordingly for nested control structures. +.B \-Wf\-a +causes +.I lcc +to read a +.B prof.out +file from a previous execution and to use the data therein +to compute reference counts (see +.BR \-b ). +.PP +.I lcc +is a cross compiler; +.BI \-Wf\-target= target/os +causes +.I lcc +to generate code for +.I target +running the operating system denoted by +.IR os . +The supported +.I target/os +combinations may include +.PP +.RS +.ta \w'sparc/solarisxx'u +.nf +alpha/osf ALPHA, OSF 3.2 +mips/irix big-endian MIPS, IRIX 5.2 +mips/ultrix little-endian MIPS, ULTRIX 4.3 +sparc/solaris SPARC, Solaris 2.3 +x86/win32 x86, Windows NT 4.0/Windows 95/98 +x86/linux x86, Linux +symbolic text rendition of the generated code +null no output +.fi +.RE +.PP +For +.BR \-Wf\-target=symbolic , +the option +.B \-Wf-html +causes the text rendition to be emitted as HTML. +.B +.SH LIMITATIONS +.PP +.I lcc +accepts the C programming language +as described in the ANSI standard. +If +.I lcc +is used with the GNU C preprocessor, the +.B \-Wp\-trigraphs +option is required to enable trigraph sequences. +.PP +Plain int bit fields are signed. +Bit fields are aligned like unsigned integers but are otherwise laid out +as by most standard C compilers. +Some compilers, such as the GNU C compiler, +may choose other, incompatible layouts. +.PP +Likewise, calling conventions are intended to be compatible with +the host C compiler, +except possibly for passing and returning structures. +Specifically, +.I lcc +passes and returns structures like host ANSI C compilers +on most targets, but some older host C compilers use different conventions. +Consequently, calls to/from such functions compiled with +older C compilers may not work. +Calling a function that returns +a structure without declaring it as such violates +the ANSI standard and may cause a fault. +.SH FILES +.PP +The file names listed below are +.IR typical , +but vary among installations; installation-dependent variants +can be displayed by running +.I lcc +with the +.B \-v +option. +.PP +.RS +.ta \w'$LCCDIR/liblcc.{a,lib}XX'u +.nf +file.{c,C} input file +file.{s,asm} assembly-language file +file.{o,obj} object file +a.{out,exe} loaded output +/tmp/lcc* temporary files +$LCCDIR/cpp preprocessor +$LCCDIR/rcc compiler +$LCCDIR/liblcc.{a,lib} \fIlcc\fP-specific library +/lib/crt0.o runtime startup (UNIX) +/lib/[gm]crt0.o startups for profiling (UNIX) +/lib/libc.a standard library (UNIX) +$LCCDIR/include ANSI standard headers +/usr/local/include local headers +/usr/include traditional headers +prof.out file produced for \fIbprint\fR(1) +mon.out file produced for \fIprof\fR(1) +gmon.out file produced for \fIgprof\fR(1) +.fi +.RE +.PP +.I lcc +predefines the macro +.B __LCC__ +on all systems. +It may also predefine some installation-dependent symbols; option +.B \-v +exposes them. +.SH "SEE ALSO" +.PP +C. W. Fraser and D. R. Hanson, +.I A Retargetable C Compiler: Design and Implementation, +Addison-Wesley, 1995. ISBN 0-8053-1670-1. +.PP +The World-Wide Web page at http://www.cs.princeton.edu/software/lcc/. +.PP +S. P. Harbison and G. L. Steele, Jr., +.I C: A Reference Manual, +4th ed., Prentice-Hall, 1995. +.PP +B. W. Kernighan and D. M. Ritchie, +.I The C Programming Language, +2nd ed., Prentice-Hall, 1988. +.PP +American National Standards Inst., +.I American National Standard for Information Systems\(emProgramming +.IR Language\(emC , +ANSI X3.159-1989, New York, 1990. +.br +.SH BUGS +Mail bug reports along with the shortest preprocessed program +that exposes them and the details reported by +.IR lcc 's +.B \-v +option to lcc-bugs@princeton.edu. The WWW page at +URL http://www.cs.princeton.edu/software/lcc/ +includes detailed instructions for reporting bugs. +.PP +The ANSI standard headers conform to the specifications in +the Standard, which may be too restrictive for some applications, +but necessary for portability. +Functions given in the ANSI headers may be missing from +some local C libraries (e.g., wide-character functions) +or may not correspond exactly to the local versions; +for example, the ANSI standard +stdio.h +specifies that +.IR printf , +.IR fprintf , +and +.I sprintf +return the number of characters written to the file or array, +but some existing libraries don't implement this convention. +.PP +On the MIPS and SPARC, old-style variadic functions must use +varargs.h +from MIPS or Sun. New-style is recommended. +.PP +With +.BR \-b , +files compiled +.I without +.B \-b +may cause +.I bprint +to print erroneous call graphs. +For example, if +.B f +calls +.B g +calls +.B h +and +.B f +and +.B h +are compiled with +.BR \-b , +but +.B g +is not, +.B bprint +will report that +.B f +called +.BR h . +The total number of calls is correct, however. diff --git a/src/tools/lcc/doc/lcc.pdf b/src/tools/lcc/doc/lcc.pdf Binary files differnew file mode 100644 index 0000000..6134de6 --- /dev/null +++ b/src/tools/lcc/doc/lcc.pdf diff --git a/src/tools/lcc/etc/bytecode.c b/src/tools/lcc/etc/bytecode.c new file mode 100644 index 0000000..fe4178f --- /dev/null +++ b/src/tools/lcc/etc/bytecode.c @@ -0,0 +1,66 @@ +/* quake3 bytecode target */ + +#include <string.h> +#include <stdio.h> +#include "../../../qcommon/q_platform.h" + +#ifdef _WIN32 +#define BINEXT ".exe" +#else +#define BINEXT "" +#endif + +char *suffixes[] = { ".c", ".i", ".asm", ".o", ".out", 0 }; +char inputs[256] = ""; +char *cpp[] = { "q3cpp" BINEXT, + "-D__STDC__=1", "-D__STRICT_ANSI__", "-D__signed__=signed", "-DQ3_VM", + "$1", "$2", "$3", 0 }; +char *include[] = { 0 }; +char *com[] = { "q3rcc" BINEXT, "-target=bytecode", "$1", "$2", "$3", 0 }; +char *ld[] = { 0 }; +char *as[] = { 0 }; + +extern char *concat(char *, char *); + +/* +=============== +UpdatePaths + +Updates the paths to q3cpp and q3rcc based on +the directory that contains q3lcc +=============== +*/ +void UpdatePaths( const char *lccBinary ) +{ + char basepath[ 1024 ]; + char *p; + + strncpy( basepath, lccBinary, 1024 ); + p = strrchr( basepath, PATH_SEP ); + + if( p ) + { + *( p + 1 ) = '\0'; + + cpp[ 0 ] = concat( basepath, "q3cpp" BINEXT ); + com[ 0 ] = concat( basepath, "q3rcc" BINEXT ); + } +} + +int option(char *arg) { + if (strncmp(arg, "-lccdir=", 8) == 0) { + cpp[0] = concat(&arg[8], "/q3cpp" BINEXT); + include[0] = concat("-I", concat(&arg[8], "/include")); + com[0] = concat(&arg[8], "/q3rcc" BINEXT); + } else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-pg") == 0) { + fprintf( stderr, "no profiling supported, %s ignored.\n", arg); + } else if (strcmp(arg, "-b") == 0) + ; + else if (strcmp(arg, "-g") == 0) + fprintf( stderr, "no debugging supported, %s ignored.\n", arg); + else if (strncmp(arg, "-ld=", 4) == 0 || strcmp(arg, "-static") == 0) { + fprintf( stderr, "no linking supported, %s ignored.\n", arg); + } else + return 0; + return 1; +} diff --git a/src/tools/lcc/etc/lcc.c b/src/tools/lcc/etc/lcc.c new file mode 100644 index 0000000..a168852 --- /dev/null +++ b/src/tools/lcc/etc/lcc.c @@ -0,0 +1,853 @@ +/* + * lcc [ option ]... [ file | -llib ]... + * front end for the ANSI C compiler + */ +static char rcsid[] = "Id: dummy rcsid"; + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <signal.h> +#include <unistd.h> + +#ifndef TEMPDIR +#define TEMPDIR "/tmp" +#endif + +typedef struct list *List; +struct list { /* circular list nodes: */ + char *str; /* option or file name */ + List link; /* next list element */ +}; + +static void *alloc(int); +static List append(char *,List); +extern char *basename(char *); +static int callsys(char *[]); +extern char *concat(char *, char *); +static int compile(char *, char *); +static void compose(char *[], List, List, List); +static void error(char *, char *); +static char *exists(char *); +static char *first(char *); +static int filename(char *, char *); +static List find(char *, List); +static void help(void); +static void initinputs(void); +static void interrupt(int); +static void opt(char *); +static List path2list(const char *); +extern int main(int, char *[]); +extern char *replace(const char *, int, int); +static void rm(List); +extern char *strsave(const char *); +extern char *stringf(const char *, ...); +extern int suffix(char *, char *[], int); +extern char *tempname(char *); + +#ifndef __sun +extern int getpid(void); +#endif + +extern char *cpp[], *include[], *com[], *as[],*ld[], inputs[], *suffixes[]; +extern int option(char *); + +static int errcnt; /* number of errors */ +static int Eflag; /* -E specified */ +static int Sflag = 1; /* -S specified */ //for Q3 we always generate asm +static int cflag; /* -c specified */ +static int verbose; /* incremented for each -v */ +static List llist[2]; /* loader files, flags */ +static List alist; /* assembler flags */ +static List clist; /* compiler flags */ +static List plist; /* preprocessor flags */ +static List ilist; /* list of additional includes from LCCINPUTS */ +static List rmlist; /* list of files to remove */ +static char *outfile; /* ld output file or -[cS] object file */ +static int ac; /* argument count */ +static char **av; /* argument vector */ +char *tempdir = TEMPDIR; /* directory for temporary files */ +static char *progname; +static List lccinputs; /* list of input directories */ + +extern void UpdatePaths( const char *lccBinary ); + +int main(int argc, char *argv[]) { + int i, j, nf; + + progname = argv[0]; + + UpdatePaths( progname ); + + ac = argc + 50; + av = alloc(ac*sizeof(char *)); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, interrupt); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, interrupt); +#ifdef SIGHUP + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, interrupt); +#endif + if (getenv("TMP")) + tempdir = getenv("TMP"); + else if (getenv("TEMP")) + tempdir = getenv("TEMP"); + else if (getenv("TMPDIR")) + tempdir = getenv("TMPDIR"); + assert(tempdir); + i = strlen(tempdir); + for (; (i > 0 && tempdir[i-1] == '/') || tempdir[i-1] == '\\'; i--) + tempdir[i-1] = '\0'; + if (argc <= 1) { + help(); + exit(0); + } + plist = append("-D__LCC__", 0); + initinputs(); + if (getenv("LCCDIR")) + option(stringf("-lccdir=%s", getenv("LCCDIR"))); + for (nf = 0, i = j = 1; i < argc; i++) { + if (strcmp(argv[i], "-o") == 0) { + if (++i < argc) { + if (suffix(argv[i], suffixes, 2) >= 0) { + error("-o would overwrite %s", argv[i]); + exit(8); + } + outfile = argv[i]; + continue; + } else { + error("unrecognized option `%s'", argv[i-1]); + exit(8); + } + } else if (strcmp(argv[i], "-target") == 0) { + if (argv[i+1] && *argv[i+1] != '-') + i++; + continue; + } else if (*argv[i] == '-' && argv[i][1] != 'l') { + opt(argv[i]); + continue; + } else if (*argv[i] != '-' && suffix(argv[i], suffixes, 3) >= 0) + nf++; + argv[j++] = argv[i]; + } + if ((cflag || Sflag) && outfile && nf != 1) { + fprintf(stderr, "%s: -o %s ignored\n", progname, outfile); + outfile = 0; + } + argv[j] = 0; + for (i = 0; include[i]; i++) + plist = append(include[i], plist); + if (ilist) { + List b = ilist; + do { + b = b->link; + plist = append(b->str, plist); + } while (b != ilist); + } + ilist = 0; + for (i = 1; argv[i]; i++) + if (*argv[i] == '-') + opt(argv[i]); + else { + char *name = exists(argv[i]); + if (name) { + if (strcmp(name, argv[i]) != 0 + || (nf > 1 && suffix(name, suffixes, 3) >= 0)) + fprintf(stderr, "%s:\n", name); + filename(name, 0); + } else + error("can't find `%s'", argv[i]); + } + if (errcnt == 0 && !Eflag && !Sflag && !cflag && llist[1]) { + compose(ld, llist[0], llist[1], + append(outfile ? outfile : concat("a", first(suffixes[4])), 0)); + if (callsys(av)) + errcnt++; + } + rm(rmlist); + return errcnt ? EXIT_FAILURE : EXIT_SUCCESS; +} + +/* alloc - allocate n bytes or die */ +static void *alloc(int n) { + static char *avail, *limit; + + n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1); + if (n >= limit - avail) { + avail = malloc(n + 4*1024); + assert(avail); + limit = avail + n + 4*1024; + } + avail += n; + return avail - n; +} + +/* append - append a node with string str onto list, return new list */ +static List append(char *str, List list) { + List p = alloc(sizeof *p); + + p->str = str; + if (list) { + p->link = list->link; + list->link = p; + } else + p->link = p; + return p; +} + +/* basename - return base name for name, e.g. /usr/drh/foo.c => foo */ +char *basename(char *name) { + char *s, *b, *t = 0; + + for (b = s = name; *s; s++) + if (*s == '/' || *s == '\\') { + b = s + 1; + t = 0; + } else if (*s == '.') + t = s; + s = strsave(b); + if (t) + s[t-b] = 0; + return s; +} + +#ifdef WIN32 +#include <process.h> + +static char *escapeDoubleQuotes(const char *string) { + int stringLength = strlen(string); + int bufferSize = stringLength + 1; + int i, j; + char *newString; + + if (string == NULL) + return NULL; + + for (i = 0; i < stringLength; i++) { + if (string[i] == '"') + bufferSize++; + } + + newString = (char*)malloc(bufferSize); + + if (newString == NULL) + return NULL; + + for (i = 0, j = 0; i < stringLength; i++) { + if (string[i] == '"') + newString[j++] = '\\'; + + newString[j++] = string[i]; + } + + newString[j] = '\0'; + + return newString; +} + +static int spawn(const char *cmdname, char **argv) { + int argc = 0; + char **newArgv = argv; + int i; + intptr_t exitStatus; + + // _spawnvp removes double quotes from arguments, so we + // have to escape them manually + while (*newArgv++ != NULL) + argc++; + + newArgv = (char **)malloc(sizeof(char*) * (argc + 1)); + + for (i = 0; i < argc; i++) + newArgv[i] = escapeDoubleQuotes(argv[i]); + + newArgv[argc] = NULL; + + exitStatus = _spawnvp(_P_WAIT, cmdname, (const char *const *)newArgv); + + for (i = 0; i < argc; i++) + free(newArgv[i]); + + free(newArgv); + return exitStatus; +} + +#else + +#define _P_WAIT 0 +#ifndef __sun +extern int fork(void); +#endif +extern int wait(int *); + +static int spawn(const char *cmdname, char **argv) { + int pid, n, status; + + switch (pid = fork()) { + case -1: + fprintf(stderr, "%s: no more processes\n", progname); + return 100; + case 0: + // TTimo removing hardcoded paths, searching in $PATH + execvp(cmdname, argv); + fprintf(stderr, "%s: ", progname); + perror(cmdname); + fflush(stdout); + exit(100); + } + while ((n = wait(&status)) != pid && n != -1) + ; + if (n == -1) + status = -1; + if (status&0377) { + fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname); + status |= 0400; + } + return (status>>8)&0377; +} +#endif + +/* callsys - execute the command described by av[0...], return status */ +static int callsys(char **av) { + int i, status = 0; + static char **argv; + static int argc; + char *executable; + + for (i = 0; av[i] != NULL; i++) + ; + if (i + 1 > argc) { + argc = i + 1; + if (argv == NULL) + argv = malloc(argc*sizeof *argv); + else + argv = realloc(argv, argc*sizeof *argv); + assert(argv); + } + for (i = 0; status == 0 && av[i] != NULL; ) { + int j = 0; + char *s = NULL; + for ( ; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++) + argv[j++] = av[i]; + if (s != NULL) { + if (s > av[i]) + argv[j++] = stringf("%.*s", s - av[i], av[i]); + if (s[1] != '\0') + av[i] = s + 1; + else + i++; + } + argv[j] = NULL; + executable = strsave( argv[0] ); + argv[0] = stringf( "\"%s\"", argv[0] ); + if (verbose > 0) { + int k; + fprintf(stderr, "%s", argv[0]); + for (k = 1; argv[k] != NULL; k++) + fprintf(stderr, " %s", argv[k]); + fprintf(stderr, "\n"); + } + if (verbose < 2) + status = spawn(executable, argv); + if (status == -1) { + fprintf(stderr, "%s: ", progname); + perror(argv[0]); + } + } + return status; +} + +/* concat - return concatenation of strings s1 and s2 */ +char *concat(char *s1, char *s2) { + int n = strlen(s1); + char *s = alloc(n + strlen(s2) + 1); + + strcpy(s, s1); + strcpy(s + n, s2); + return s; +} + +/* compile - compile src into dst, return status */ +static int compile(char *src, char *dst) { + compose(com, clist, append(src, 0), append(dst, 0)); + return callsys(av); +} + +/* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */ +static void compose(char *cmd[], List a, List b, List c) { + int i, j; + List lists[3]; + + lists[0] = a; + lists[1] = b; + lists[2] = c; + for (i = j = 0; cmd[i]; i++) { + char *s = strchr(cmd[i], '$'); + if (s && isdigit(s[1])) { + int k = s[1] - '0'; + assert(k >=1 && k <= 3); + if ((b = lists[k-1])) { + b = b->link; + av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1); + strncpy(av[j], cmd[i], s - cmd[i]); + av[j][s-cmd[i]] = '\0'; + strcat(av[j], b->str); + strcat(av[j++], s + 2); + while (b != lists[k-1]) { + b = b->link; + assert(j < ac); + av[j++] = b->str; + }; + } + } else if (*cmd[i]) { + assert(j < ac); + av[j++] = cmd[i]; + } + } + av[j] = NULL; +} + +/* error - issue error msg according to fmt, bump error count */ +static void error(char *fmt, char *msg) { + fprintf(stderr, "%s: ", progname); + fprintf(stderr, fmt, msg); + fprintf(stderr, "\n"); + errcnt++; +} + +/* exists - if `name' readable return its path name or return null */ +static char *exists(char *name) { + List b; + + if ( (name[0] == '/' || name[0] == '\\' || name[2] == ':') + && access(name, 4) == 0) + return name; + if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':') + && (b = lccinputs)) + do { + b = b->link; + if (b->str[0]) { + char buf[1024]; + sprintf(buf, "%s/%s", b->str, name); + if (access(buf, 4) == 0) + return strsave(buf); + } else if (access(name, 4) == 0) + return name; + } while (b != lccinputs); + if (verbose > 1) + return name; + return 0; +} + +/* first - return first component in semicolon separated list */ +static char *first(char *list) { + char *s = strchr(list, ';'); + + if (s) { + char buf[1024]; + strncpy(buf, list, s-list); + buf[s-list] = '\0'; + return strsave(buf); + } else + return list; +} + +/* filename - process file name argument `name', return status */ +static int filename(char *name, char *base) { + int status = 0; + static char *stemp, *itemp; + + if (base == 0) + base = basename(name); + switch (suffix(name, suffixes, 4)) { + case 0: /* C source files */ + compose(cpp, plist, append(name, 0), 0); + if (Eflag) { + status = callsys(av); + break; + } + if (itemp == NULL) + itemp = tempname(first(suffixes[1])); + compose(cpp, plist, append(name, 0), append(itemp, 0)); + status = callsys(av); + if (status == 0) + return filename(itemp, base); + break; + case 1: /* preprocessed source files */ + if (Eflag) + break; + if (Sflag) + status = compile(name, outfile ? outfile : concat(base, first(suffixes[2]))); + else if ((status = compile(name, stemp?stemp:(stemp=tempname(first(suffixes[2]))))) == 0) + return filename(stemp, base); + break; + case 2: /* assembly language files */ + if (Eflag) + break; + if (!Sflag) { + char *ofile; + if (cflag && outfile) + ofile = outfile; + else if (cflag) + ofile = concat(base, first(suffixes[3])); + else + ofile = tempname(first(suffixes[3])); + compose(as, alist, append(name, 0), append(ofile, 0)); + status = callsys(av); + if (!find(ofile, llist[1])) + llist[1] = append(ofile, llist[1]); + } + break; + case 3: /* object files */ + if (!find(name, llist[1])) + llist[1] = append(name, llist[1]); + break; + default: + if (Eflag) { + compose(cpp, plist, append(name, 0), 0); + status = callsys(av); + } + llist[1] = append(name, llist[1]); + break; + } + if (status) + errcnt++; + return status; +} + +/* find - find 1st occurrence of str in list, return list node or 0 */ +static List find(char *str, List list) { + List b; + + if ((b = list)) + do { + if (strcmp(str, b->str) == 0) + return b; + } while ((b = b->link) != list); + return 0; +} + +/* help - print help message */ +static void help(void) { + static char *msgs[] = { +"", " [ option | file ]...\n", +" except for -l, options are processed left-to-right before files\n", +" unrecognized options are taken to be linker options\n", +"-A warn about nonANSI usage; 2nd -A warns more\n", +"-b emit expression-level profiling code; see bprint(1)\n", +#ifdef sparc +"-Bstatic -Bdynamic specify static or dynamic libraries\n", +#endif +"-Bdir/ use the compiler named `dir/rcc'\n", +"-c compile only\n", +"-dn set switch statement density to `n'\n", +"-Dname -Dname=def define the preprocessor symbol `name'\n", +"-E run only the preprocessor on the named C programs and unsuffixed files\n", +"-g produce symbol table information for debuggers\n", +"-help or -? print this message\n", +"-Idir add `dir' to the beginning of the list of #include directories\n", +"-lx search library `x'\n", +"-N do not search the standard directories for #include files\n", +"-n emit code to check for dereferencing zero pointers\n", +"-O is ignored\n", +"-o file leave the output in `file'\n", +"-P print ANSI-style declarations for globals\n", +"-p -pg emit profiling code; see prof(1) and gprof(1)\n", +"-S compile to assembly language\n", +#ifdef linux +"-static specify static libraries (default is dynamic)\n", +#endif +"-t -tname emit function tracing calls to printf or to `name'\n", +"-target name is ignored\n", +"-tempdir=dir place temporary files in `dir/'", "\n" +"-Uname undefine the preprocessor symbol `name'\n", +"-v show commands as they are executed; 2nd -v suppresses execution\n", +"-w suppress warnings\n", +"-Woarg specify system-specific `arg'\n", +"-W[pfal]arg pass `arg' to the preprocessor, compiler, assembler, or linker\n", + 0 }; + int i; + char *s; + + msgs[0] = progname; + for (i = 0; msgs[i]; i++) { + fprintf(stderr, "%s", msgs[i]); + if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir) + fprintf(stderr, "; default=%s", tempdir); + } +#define xx(v) if ((s = getenv(#v))) fprintf(stderr, #v "=%s\n", s) + xx(LCCINPUTS); + xx(LCCDIR); +#undef xx +} + +/* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */ +static void initinputs(void) { + char *s = getenv("LCCINPUTS"); + List b; + + if (s == 0 || (s = inputs)[0] == 0) + s = "."; + if (s) { + lccinputs = path2list(s); + if ((b = lccinputs)) + do { + b = b->link; + if (strcmp(b->str, ".") != 0) { + ilist = append(concat("-I", b->str), ilist); + if (strstr(com[1], "win32") == NULL) + llist[0] = append(concat("-L", b->str), llist[0]); + } else + b->str = ""; + } while (b != lccinputs); + } +} + +/* interrupt - catch interrupt signals */ +static void interrupt(int n) { + rm(rmlist); + exit(n = 100); +} + +/* opt - process option in arg */ +static void opt(char *arg) { + switch (arg[1]) { /* multi-character options */ + case 'W': /* -Wxarg */ + if (arg[2] && arg[3]) + switch (arg[2]) { + case 'o': + if (option(&arg[3])) + return; + break; + case 'p': + plist = append(&arg[3], plist); + return; + case 'f': + if (strcmp(&arg[3], "-C") || option("-b")) { + clist = append(&arg[3], clist); + return; + } + break; /* and fall thru */ + case 'a': + alist = append(&arg[3], alist); + return; + case 'l': + llist[0] = append(&arg[3], llist[0]); + return; + } + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + case 'd': /* -dn */ + arg[1] = 's'; + clist = append(arg, clist); + return; + case 't': /* -t -tname -tempdir=dir */ + if (strncmp(arg, "-tempdir=", 9) == 0) + tempdir = arg + 9; + else + clist = append(arg, clist); + return; + case 'p': /* -p -pg */ + if (option(arg)) + clist = append(arg, clist); + else + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + case 'D': /* -Dname -Dname=def */ + case 'U': /* -Uname */ + case 'I': /* -Idir */ + plist = append(arg, plist); + return; + case 'B': /* -Bdir -Bstatic -Bdynamic */ +#ifdef sparc + if (strcmp(arg, "-Bstatic") == 0 || strcmp(arg, "-Bdynamic") == 0) + llist[1] = append(arg, llist[1]); + else +#endif + { + static char *path; + if (path) + error("-B overwrites earlier option", 0); + path = arg + 2; + if (strstr(com[1], "win32") != NULL) + com[0] = concat(replace(path, '/', '\\'), concat("rcc", first(suffixes[4]))); + else + com[0] = concat(path, "rcc"); + if (path[0] == 0) + error("missing directory in -B option", 0); + } + return; + case 'h': + if (strcmp(arg, "-help") == 0) { + static int printed = 0; + case '?': + if (!printed) + help(); + printed = 1; + return; + } +#ifdef linux + case 's': + if (strcmp(arg,"-static") == 0) { + if (!option(arg)) + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + } +#endif + } + if (arg[2] == 0) + switch (arg[1]) { /* single-character options */ + case 'S': + Sflag++; + return; + case 'O': + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + case 'A': case 'n': case 'w': case 'P': + clist = append(arg, clist); + return; + case 'g': case 'b': + if (option(arg)) + clist = append(arg[1] == 'g' ? "-g2" : arg, clist); + else + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + case 'G': + if (option(arg)) { + clist = append("-g3", clist); + llist[0] = append("-N", llist[0]); + } else + fprintf(stderr, "%s: %s ignored\n", progname, arg); + return; + case 'E': + Eflag++; + return; + case 'c': + cflag++; + return; + case 'N': + if (strcmp(basename(cpp[0]), "gcc-cpp") == 0) + plist = append("-nostdinc", plist); + include[0] = 0; + ilist = 0; + return; + case 'v': + if (verbose++ == 0) { + if (strcmp(basename(cpp[0]), "gcc-cpp") == 0) + plist = append(arg, plist); + clist = append(arg, clist); + fprintf(stderr, "%s %s\n", progname, rcsid); + } + return; + } + if (cflag || Sflag || Eflag) + fprintf(stderr, "%s: %s ignored\n", progname, arg); + else + llist[1] = append(arg, llist[1]); +} + +/* path2list - convert a colon- or semicolon-separated list to a list */ +static List path2list(const char *path) { + List list = NULL; + char sep = ':'; + + if (path == NULL) + return NULL; + if (strchr(path, ';')) + sep = ';'; + while (*path) { + char *p, buf[512]; + if ((p = strchr(path, sep))) { + assert(p - path < sizeof buf); + strncpy(buf, path, p - path); + buf[p-path] = '\0'; + } else { + assert(strlen(path) < sizeof buf); + strcpy(buf, path); + } + if (!find(buf, list)) + list = append(strsave(buf), list); + if (p == 0) + break; + path = p + 1; + } + return list; +} + +/* replace - copy str, then replace occurrences of from with to, return the copy */ +char *replace(const char *str, int from, int to) { + char *s = strsave(str), *p = s; + + for ( ; (p = strchr(p, from)) != NULL; p++) + *p = to; + return s; +} + +/* rm - remove files in list */ +static void rm(List list) { + if (list) { + List b = list; + if (verbose) + fprintf(stderr, "rm"); + do { + if (verbose) + fprintf(stderr, " %s", b->str); + if (verbose < 2) + remove(b->str); + } while ((b = b->link) != list); + if (verbose) + fprintf(stderr, "\n"); + } +} + +/* strsave - return a saved copy of string str */ +char *strsave(const char *str) { + return strcpy(alloc(strlen(str)+1), str); +} + +/* stringf - format and return a string */ +char *stringf(const char *fmt, ...) { + char buf[1024]; + va_list ap; + int n; + + va_start(ap, fmt); + n = vsprintf(buf, fmt, ap); + va_end(ap); + return strsave(buf); +} + +/* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */ +int suffix(char *name, char *tails[], int n) { + int i, len = strlen(name); + + for (i = 0; i < n; i++) { + char *s = tails[i], *t; + for ( ; (t = strchr(s, ';')); s = t + 1) { + int m = t - s; + if (len > m && strncmp(&name[len-m], s, m) == 0) + return i; + } + if (*s) { + int m = strlen(s); + if (len > m && strncmp(&name[len-m], s, m) == 0) + return i; + } + } + return -1; +} + +/* tempname - generate a temporary file name in tempdir with given suffix */ +char *tempname(char *suffix) { + static int n; + char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix); + + if (strstr(com[1], "win32") != NULL) + name = replace(name, '/', '\\'); + rmlist = append(name, rmlist); + return name; +} diff --git a/src/tools/lcc/lburg/gram.c b/src/tools/lcc/lburg/gram.c new file mode 100644 index 0000000..79a9d84 --- /dev/null +++ b/src/tools/lcc/lburg/gram.c @@ -0,0 +1,1817 @@ +/* A Bison parser, made by GNU Bison 2.4.2. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Copy the first part of user declarations. */ + +/* Line 189 of yacc.c */ +#line 1 "src/tools/lcc/lburg/gram.y" + +#include <stdio.h> +#include "lburg.h" +static char rcsid[] = "$Id: gram.y 145 2001-10-17 21:53:10Z timo $"; +/*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */ +static int yylineno = 0; + + +/* Line 189 of yacc.c */ +#line 81 "y.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TERMINAL = 258, + START = 259, + PPERCENT = 260, + ID = 261, + TEMPLATE = 262, + CODE = 263, + INT = 264 + }; +#endif +/* Tokens. */ +#define TERMINAL 258 +#define START 259 +#define PPERCENT 260 +#define ID 261 +#define TEMPLATE 262 +#define CODE 263 +#define INT 264 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 214 of yacc.c */ +#line 8 "src/tools/lcc/lburg/gram.y" + + int n; + char *string; + Tree tree; + + + +/* Line 214 of yacc.c */ +#line 143 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 155 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 35 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 16 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 9 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 20 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 37 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 264 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 14, 2, 2, 15, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 12, 2, + 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 7, 9, 10, 13, 17, 21, 23, + 26, 27, 32, 33, 41, 44, 48, 50, 52, 57, + 64 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 17, 0, -1, 18, 5, 21, -1, 18, -1, -1, + 18, 19, -1, 3, 20, 10, -1, 4, 22, 10, + -1, 10, -1, 1, 10, -1, -1, 20, 6, 11, + 9, -1, -1, 21, 22, 12, 23, 7, 24, 10, + -1, 21, 10, -1, 21, 1, 10, -1, 6, -1, + 6, -1, 6, 13, 23, 14, -1, 6, 13, 23, + 15, 23, 14, -1, 8, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 22, 22, 23, 26, 27, 30, 31, 35, 36, + 39, 40, 43, 44, 45, 46, 49, 52, 53, 54, + 57 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "TERMINAL", "START", "PPERCENT", "ID", + "TEMPLATE", "CODE", "INT", "'\\n'", "'='", "':'", "'('", "')'", "','", + "$accept", "spec", "decls", "decl", "blist", "rules", "nonterm", "tree", + "cost", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 10, 61, 58, 40, 41, 44 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 16, 17, 17, 18, 18, 19, 19, 19, 19, + 20, 20, 21, 21, 21, 21, 22, 23, 23, 23, + 24 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 3, 1, 0, 2, 3, 3, 1, 2, + 0, 4, 0, 7, 2, 3, 1, 1, 4, 6, + 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 4, 0, 0, 1, 0, 10, 0, 12, 8, 5, + 9, 0, 16, 0, 0, 0, 6, 7, 0, 14, + 0, 0, 15, 0, 11, 17, 0, 0, 0, 0, + 20, 0, 18, 0, 13, 0, 19 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 2, 9, 11, 14, 13, 26, 31 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -26 +static const yytype_int8 yypact[] = +{ + -26, 11, 0, -26, 5, -26, 8, -26, -26, -26, + -26, 3, -26, 7, 6, 9, -26, -26, 12, -26, + 13, 14, -26, 15, -26, 16, 17, 15, 18, 4, + -26, 20, -26, 15, -26, 19, -26 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -26, -26, -26, -26, -26, -26, 21, -25, -26 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -4 +static const yytype_int8 yytable[] = +{ + -3, 4, 29, 5, 6, 7, -2, 18, 35, 15, + 8, 3, 12, 16, 12, 10, 19, 17, 32, 33, + 21, 25, 22, 24, 28, 23, 30, 0, 0, 27, + 34, 0, 0, 36, 0, 20 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 1, 27, 3, 4, 5, 0, 1, 33, 6, + 10, 0, 6, 10, 6, 10, 10, 10, 14, 15, + 11, 6, 10, 9, 7, 12, 8, -1, -1, 13, + 10, -1, -1, 14, -1, 14 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 17, 18, 0, 1, 3, 4, 5, 10, 19, + 10, 20, 6, 22, 21, 6, 10, 10, 1, 10, + 22, 11, 10, 12, 9, 6, 23, 13, 7, 23, + 8, 24, 14, 15, 10, 23, 14 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + +/* Line 1464 of yacc.c */ +#line 22 "src/tools/lcc/lburg/gram.y" + { yylineno = 0; } + break; + + case 3: + +/* Line 1464 of yacc.c */ +#line 23 "src/tools/lcc/lburg/gram.y" + { yylineno = 0; } + break; + + case 7: + +/* Line 1464 of yacc.c */ +#line 31 "src/tools/lcc/lburg/gram.y" + { + if (nonterm((yyvsp[(2) - (3)].string))->number != 1) + yyerror("redeclaration of the start symbol\n"); + } + break; + + case 9: + +/* Line 1464 of yacc.c */ +#line 36 "src/tools/lcc/lburg/gram.y" + { yyerrok; } + break; + + case 11: + +/* Line 1464 of yacc.c */ +#line 40 "src/tools/lcc/lburg/gram.y" + { term((yyvsp[(2) - (4)].string), (yyvsp[(4) - (4)].n)); } + break; + + case 13: + +/* Line 1464 of yacc.c */ +#line 44 "src/tools/lcc/lburg/gram.y" + { rule((yyvsp[(2) - (7)].string), (yyvsp[(4) - (7)].tree), (yyvsp[(5) - (7)].string), (yyvsp[(6) - (7)].string)); } + break; + + case 15: + +/* Line 1464 of yacc.c */ +#line 46 "src/tools/lcc/lburg/gram.y" + { yyerrok; } + break; + + case 16: + +/* Line 1464 of yacc.c */ +#line 49 "src/tools/lcc/lburg/gram.y" + { nonterm((yyval.string) = (yyvsp[(1) - (1)].string)); } + break; + + case 17: + +/* Line 1464 of yacc.c */ +#line 52 "src/tools/lcc/lburg/gram.y" + { (yyval.tree) = tree((yyvsp[(1) - (1)].string), 0, 0); } + break; + + case 18: + +/* Line 1464 of yacc.c */ +#line 53 "src/tools/lcc/lburg/gram.y" + { (yyval.tree) = tree((yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].tree), 0); } + break; + + case 19: + +/* Line 1464 of yacc.c */ +#line 54 "src/tools/lcc/lburg/gram.y" + { (yyval.tree) = tree((yyvsp[(1) - (6)].string), (yyvsp[(3) - (6)].tree), (yyvsp[(5) - (6)].tree)); } + break; + + case 20: + +/* Line 1464 of yacc.c */ +#line 57 "src/tools/lcc/lburg/gram.y" + { if (*(yyvsp[(1) - (1)].string) == 0) (yyval.string) = "0"; } + break; + + + +/* Line 1464 of yacc.c */ +#line 1461 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + +/* Line 1684 of yacc.c */ +#line 59 "src/tools/lcc/lburg/gram.y" + +#include <assert.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> + +int errcnt = 0; +FILE *infp = NULL; +FILE *outfp = NULL; +static char buf[BUFSIZ], *bp = buf; +static int ppercent = 0; +static int code = 0; + +static int get(void) { + if (*bp == 0) { + bp = buf; + *bp = 0; + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') { + for (;;) { + if (fgets(buf, sizeof buf, infp) == NULL) { + yywarn("unterminated %{...%}\n"); + return EOF; + } + yylineno++; + if (strcmp(buf, "%}\n") == 0) + break; + fputs(buf, outfp); + } + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + } + } + return *bp++; +} + +void yyerror(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + vfprintf(stderr, fmt, ap); + if (fmt[strlen(fmt)-1] != '\n') + fprintf(stderr, "\n"); + errcnt++; + va_end(ap); +} + +int yylex(void) { + int c; + + if (code) { + char *p; + bp += strspn(bp, " \t\f"); + p = strchr(bp, '\n'); + if (p == NULL) + p = strchr(bp, '\n'); + while (p > bp && isspace(p[-1])) + p--; + yylval.string = alloc(p - bp + 1); + strncpy(yylval.string, bp, p - bp); + yylval.string[p - bp] = 0; + bp = p; + code--; + return CODE; + } + while ((c = get()) != EOF) { + switch (c) { + case ' ': case '\f': case '\t': + continue; + case '\n': + case '(': case ')': case ',': + case ':': case '=': + return c; + } + if (c == '%' && *bp == '%') { + bp++; + return ppercent++ ? 0 : PPERCENT; + } else if (c == '%' && strncmp(bp, "term", 4) == 0 + && isspace(bp[4])) { + bp += 4; + return TERMINAL; + } else if (c == '%' && strncmp(bp, "start", 5) == 0 + && isspace(bp[5])) { + bp += 5; + return START; + } else if (c == '"') { + char *p = strchr(bp, '"'); + if (p == NULL) { + yyerror("missing \" in assembler template\n"); + p = strchr(bp, '\n'); + if (p == NULL) + p = strchr(bp, '\0'); + } + assert(p); + yylval.string = alloc(p - bp + 1); + strncpy(yylval.string, bp, p - bp); + yylval.string[p - bp] = 0; + bp = *p == '"' ? p + 1 : p; + code++; + return TEMPLATE; + } else if (isdigit(c)) { + int n = 0; + do { + int d = c - '0'; + if (n > (INT_MAX - d)/10) + yyerror("integer greater than %d\n", INT_MAX); + else + n = 10*n + d; + c = get(); + } while (c != EOF && isdigit(c)); + bp--; + yylval.n = n; + return INT; + } else if (isalpha(c)) { + char *p = bp - 1; + while (isalpha(*bp) || isdigit(*bp) || *bp == '_') + bp++; + yylval.string = alloc(bp - p + 1); + strncpy(yylval.string, p, bp - p); + yylval.string[bp - p] = 0; + return ID; + } else if (isprint(c)) + yyerror("invalid character `%c'\n", c); + else + yyerror("invalid character `\\%03o'\n", (unsigned char)c); + } + return 0; +} + +void yywarn(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); +} + diff --git a/src/tools/lcc/lburg/gram.y b/src/tools/lcc/lburg/gram.y new file mode 100644 index 0000000..1ecd8a9 --- /dev/null +++ b/src/tools/lcc/lburg/gram.y @@ -0,0 +1,202 @@ +%{ +#include <stdio.h> +#include "lburg.h" +static char rcsid[] = "$Id: gram.y 145 2001-10-17 21:53:10Z timo $"; +/*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */ +static int yylineno = 0; +%} +%union { + int n; + char *string; + Tree tree; +} +%term TERMINAL +%term START +%term PPERCENT + +%token <string> ID TEMPLATE CODE +%token <n> INT +%type <string> nonterm cost +%type <tree> tree +%% +spec : decls PPERCENT rules { yylineno = 0; } + | decls { yylineno = 0; } + ; + +decls : /* lambda */ + | decls decl + ; + +decl : TERMINAL blist '\n' + | START nonterm '\n' { + if (nonterm($2)->number != 1) + yyerror("redeclaration of the start symbol\n"); + } + | '\n' + | error '\n' { yyerrok; } + ; + +blist : /* lambda */ + | blist ID '=' INT { term($2, $4); } + ; + +rules : /* lambda */ + | rules nonterm ':' tree TEMPLATE cost '\n' { rule($2, $4, $5, $6); } + | rules '\n' + | rules error '\n' { yyerrok; } + ; + +nonterm : ID { nonterm($$ = $1); } + ; + +tree : ID { $$ = tree($1, 0, 0); } + | ID '(' tree ')' { $$ = tree($1, $3, 0); } + | ID '(' tree ',' tree ')' { $$ = tree($1, $3, $5); } + ; + +cost : CODE { if (*$1 == 0) $$ = "0"; } + ; +%% +#include <assert.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> + +int errcnt = 0; +FILE *infp = NULL; +FILE *outfp = NULL; +static char buf[BUFSIZ], *bp = buf; +static int ppercent = 0; +static int code = 0; + +static int get(void) { + if (*bp == 0) { + bp = buf; + *bp = 0; + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') { + for (;;) { + if (fgets(buf, sizeof buf, infp) == NULL) { + yywarn("unterminated %{...%}\n"); + return EOF; + } + yylineno++; + if (strcmp(buf, "%}\n") == 0) + break; + fputs(buf, outfp); + } + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + } + } + return *bp++; +} + +void yyerror(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + vfprintf(stderr, fmt, ap); + if (fmt[strlen(fmt)-1] != '\n') + fprintf(stderr, "\n"); + errcnt++; + va_end(ap); +} + +int yylex(void) { + int c; + + if (code) { + char *p; + bp += strspn(bp, " \t\f"); + p = strchr(bp, '\n'); + if (p == NULL) + p = strchr(bp, '\n'); + while (p > bp && isspace(p[-1])) + p--; + yylval.string = alloc(p - bp + 1); + strncpy(yylval.string, bp, p - bp); + yylval.string[p - bp] = 0; + bp = p; + code--; + return CODE; + } + while ((c = get()) != EOF) { + switch (c) { + case ' ': case '\f': case '\t': + continue; + case '\n': + case '(': case ')': case ',': + case ':': case '=': + return c; + } + if (c == '%' && *bp == '%') { + bp++; + return ppercent++ ? 0 : PPERCENT; + } else if (c == '%' && strncmp(bp, "term", 4) == 0 + && isspace(bp[4])) { + bp += 4; + return TERMINAL; + } else if (c == '%' && strncmp(bp, "start", 5) == 0 + && isspace(bp[5])) { + bp += 5; + return START; + } else if (c == '"') { + char *p = strchr(bp, '"'); + if (p == NULL) { + yyerror("missing \" in assembler template\n"); + p = strchr(bp, '\n'); + if (p == NULL) + p = strchr(bp, '\0'); + } + assert(p); + yylval.string = alloc(p - bp + 1); + strncpy(yylval.string, bp, p - bp); + yylval.string[p - bp] = 0; + bp = *p == '"' ? p + 1 : p; + code++; + return TEMPLATE; + } else if (isdigit(c)) { + int n = 0; + do { + int d = c - '0'; + if (n > (INT_MAX - d)/10) + yyerror("integer greater than %d\n", INT_MAX); + else + n = 10*n + d; + c = get(); + } while (c != EOF && isdigit(c)); + bp--; + yylval.n = n; + return INT; + } else if (isalpha(c)) { + char *p = bp - 1; + while (isalpha(*bp) || isdigit(*bp) || *bp == '_') + bp++; + yylval.string = alloc(bp - p + 1); + strncpy(yylval.string, p, bp - p); + yylval.string[bp - p] = 0; + return ID; + } else if (isprint(c)) + yyerror("invalid character `%c'\n", c); + else + yyerror("invalid character `\\%03o'\n", (unsigned char)c); + } + return 0; +} + +void yywarn(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); +} diff --git a/src/tools/lcc/lburg/lburg.1 b/src/tools/lcc/lburg/lburg.1 new file mode 100644 index 0000000..8cf7250 --- /dev/null +++ b/src/tools/lcc/lburg/lburg.1 @@ -0,0 +1,179 @@ +.TH LBURG 1 "local \- 11/30/94" +.\" $Id: lburg.1 145 2001-10-17 21:53:10Z timo $ +.SH NAME +lburg \- lcc's code-generator generator +.SH SYNOPSIS +.B lburg +[ +.I option +]... +[ [ +.I input +] +.I output +] +.br +.SH DESCRIPTION +.PP +.I lburg +reads an lcc-style BURG specification from +.I input +and writes a pattern-matching code generator to +.IR output . +If +.I input +is `\-' or is omitted, +.I lburg +reads the standard input; +If +.I output +is `\-' or is omitted, +.I lburg +writes to the standard output. +.PP +.I lburg +accepts specifications that conform to the following EBNF grammar. +Terminals are enclosed in single quotes or are +given in uppercase, all other symbols are nonterminals or English phrases, +{X} denotes zero or more instances of X, and [X] denotes an optional X. +.PP +.nf +.RS +.ft CW +spec: `%{' configuration `%}' { dcl } `%%' { rule } + [ `%%' C code ] + +dcl: `%start' nonterm + `%term' { ID `=' INT } + +rule: nonterm `:' tree template [ C expression ] + +tree: term `(' tree `,' tree `)' + term `(' tree `)' + term + nonterm + +nonterm: ID + +template: `"' { any character except double quote } `"' +.RE +.fi +.PP +Specifications are structurally similar to +.IR yacc 's. +Text between +`\f(CW%{\fP' +and +`\f(CW%}\fP' +is called the configuration section; there may be several such segments. +All are concatenated and copied verbatim into the head of the output. +Text after the second +`\f(CW%%\fP', +if any, is also copied verbatim into the output, at the end. +.PP +Specifications consist of declarations, a +`\f(CW%%\fP' +separator, and rules. +Input is line-oriented; each declaration and rule must appear on a separate line, +and declarations must begin in column 1. +Declarations declare terminals \(em the operators in subject +trees \(em and associate a unique, positive external symbol +number with each one. +Nonterminals are declared by their presence +on the left side of rules. The +\f(CW%start\fP +declaration optionally declares a nonterminal as the start symbol. +In the grammar above, +\f(CWterm\fP +and +\f(CWnonterm\fP +denote identifiers that are terminals and nonterminals. +.PP +Rules define tree patterns in a fully parenthesized prefix +form. Every nonterminal denotes a tree. +Each operator has a fixed +arity, which is inferred from the rules in which it is used. +A chain rule is a rule whose pattern is another nonterminal. +If no start symbol is declared, the nonterminal defined by the first rule is used. +.PP +Each rule ends with an expression that computes the cost of matching +that rule; omitted costs +default to zero. Costs of chain rules must be constants. +.PP +The configuration section configures the output +for the trees being parsed and the client's environment. +As shown, this section must define +\f(CWNODEPTR_TYPE\fP +to be a visible typedef symbol for a pointer to a +node in the subject tree. +The labeller invokes +\f(CWOP_LABEL(p)\fP, +\f(CWLEFT\_CHILD(p)\fP, and +\f(CWRIGHT\_CHILD(p)\fP +to read the operator and children from the node pointed to by \f(CWp\fP. +If the configuration section defines these operations as macros, they are implemented in-line; +otherwise, they must be implemented as functions. +.PP +The matcher +computes and stores a single integral state in each node of the subject tree. +The configuration section must define a macro +\f(CWSTATE_LABEL(p)\fP +to access the state field of the node pointed to +by \f(CWp\fP. It must be large enough to hold a pointer, and +a macro is required because it is used as an lvalue. +.PP +.SH OPTIONS +.TP +.BI \-p \ prefix +.br +.ns +.TP +.BI \-p prefix +Use +.I prefix +as the disambiquating prefix for visible names and fields. +The default is `\f(CW_\fP'. +.TP +.B \-T +Arrange for +.sp +.nf +.ft CW + void _trace(NODEPTR_TYPE p, int eruleno, + int cost, int bestcost); +.sp +.fi +.ft R +to be called at each successful match. +\f(CWp\fP +identifies the node and +\f(CWeruleno\fP +identifies the matching rule; the rules are numbered +beginning at 1 in the order they appear in the input. +\f(CWcost\fP +is the cost of the match and +\f(CWbestcost\fP +is the cost of the best previous match. The current match +wins only if +\f(CWcost\fP +is less than \f(CWbestcost\fP. +32767 represents the infinite cost of no previous match. +\f(CW_trace\fP must be declared in the configuration section. +.SH "SEE ALSO" +.IR lcc (1) +.PP +C. W. Fraser and D. R. Hanson, +.IR A Retargetable C Compiler: Design and Implementation , +Benjamin/Cummings, Redwood City, CA, 1995, +ISBN 0-8053-1670-1. Chapter 14. +.PP +C. W. Fraser, D. R. Hanson and T. A. Proebsting, +`Engineering a simple, efficient code generator generator,' +.I +ACM Letters on Programming Languages and Systems +.BR 1 , +3 (Sep. 1992), 213-226. +.br +.SH BUGS +Mail bug reports along with the shortest input +that exposes them to drh@cs.princeton.edu. diff --git a/src/tools/lcc/lburg/lburg.c b/src/tools/lcc/lburg/lburg.c new file mode 100644 index 0000000..c43c96a --- /dev/null +++ b/src/tools/lcc/lburg/lburg.c @@ -0,0 +1,671 @@ +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include "lburg.h" + +static char rcsid[] = "lburg.c - faked rcsid"; + +static char *prefix = ""; +static int Tflag = 0; +static int ntnumber = 0; +static Nonterm start = 0; +static Term terms; +static Nonterm nts; +static Rule rules; +static int nrules; +static struct block { + struct block *link; +} *memlist; /* list of allocated blocks */ + +static char *stringf(char *fmt, ...); +static void print(char *fmt, ...); +static void ckreach(Nonterm p); +static void emitclosure(Nonterm nts); +static void emitcost(Tree t, char *v); +static void emitdefs(Nonterm nts, int ntnumber); +static void emitheader(void); +static void emitkids(Rule rules, int nrules); +static void emitnts(Rule rules, int nrules); +static void emitrecalc(char *pre, Term root, Term kid); +static void emitrecord(char *pre, Rule r, char *c, int cost); +static void emitrule(Nonterm nts); +static void emitlabel(Term terms, Nonterm start, int ntnumber); +static void emitstring(Rule rules); +static void emitstruct(Nonterm nts, int ntnumber); +static void emittest(Tree t, char *v, char *suffix); + +int main(int argc, char *argv[]) { + int c, i; + Nonterm p; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-T") == 0) + Tflag = 1; + else if (strncmp(argv[i], "-p", 2) == 0 && argv[i][2]) + prefix = &argv[i][2]; + else if (strncmp(argv[i], "-p", 2) == 0 && i + 1 < argc) + prefix = argv[++i]; + else if (*argv[i] == '-' && argv[i][1]) { + yyerror("usage: %s [-T | -p prefix]... [ [ input ] output ] \n", + argv[0]); + exit(1); + } else if (infp == NULL) { + if (strcmp(argv[i], "-") == 0) + infp = stdin; + else if ((infp = fopen(argv[i], "r")) == NULL) { + yyerror("%s: can't read `%s'\n", argv[0], argv[i]); + exit(1); + } + } else if (outfp == NULL) { + if (strcmp(argv[i], "-") == 0) + outfp = stdout; + if ((outfp = fopen(argv[i], "w")) == NULL) { + yyerror("%s: can't write `%s'\n", argv[0], argv[i]); + exit(1); + } + } + if (infp == NULL) + infp = stdin; + if (outfp == NULL) + outfp = stdout; + yyparse(); + if (start) + ckreach(start); + for (p = nts; p; p = p->link) { + if (p->rules == NULL) + yyerror("undefined nonterminal `%s'\n", p->name); + if (!p->reached) + yyerror("can't reach nonterminal `%s'\n", p->name); + } + emitheader(); + emitdefs(nts, ntnumber); + emitstruct(nts, ntnumber); + emitnts(rules, nrules); + emitstring(rules); + emitrule(nts); + emitclosure(nts); + if (start) + emitlabel(terms, start, ntnumber); + emitkids(rules, nrules); + if (!feof(infp)) + while ((c = getc(infp)) != EOF) + putc(c, outfp); + while (memlist) { /* for purify */ + struct block *q = memlist->link; + free(memlist); + memlist = q; + } + return errcnt > 0; +} + +/* alloc - allocate nbytes or issue fatal error */ +void *alloc(int nbytes) { + struct block *p = calloc(1, sizeof *p + nbytes); + + if (p == NULL) { + yyerror("out of memory\n"); + exit(1); + } + p->link = memlist; + memlist = p; + return p + 1; +} + +/* stringf - format and save a string */ +static char *stringf(char *fmt, ...) { + va_list ap; + char buf[512]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + return strcpy(alloc(strlen(buf) + 1), buf); +} + +struct entry { + union { + char *name; + struct term t; + struct nonterm nt; + } sym; + struct entry *link; +} *table[211]; +#define HASHSIZE (sizeof table/sizeof table[0]) + +/* hash - return hash number for str */ +static unsigned hash(char *str) { + unsigned h = 0; + + while (*str) + h = (h<<1) + *str++; + return h; +} + +/* lookup - lookup symbol name */ +static void *lookup(char *name) { + struct entry *p = table[hash(name)%HASHSIZE]; + + for ( ; p; p = p->link) + if (strcmp(name, p->sym.name) == 0) + return &p->sym; + return 0; +} + +/* install - install symbol name */ +static void *install(char *name) { + struct entry *p = alloc(sizeof *p); + int i = hash(name)%HASHSIZE; + + p->sym.name = name; + p->link = table[i]; + table[i] = p; + return &p->sym; +} + +/* nonterm - create a new terminal id, if necessary */ +Nonterm nonterm(char *id) { + Nonterm p = lookup(id), *q = &nts; + + if (p && p->kind == NONTERM) + return p; + if (p && p->kind == TERM) + yyerror("`%s' is a terminal\n", id); + p = install(id); + p->kind = NONTERM; + p->number = ++ntnumber; + if (p->number == 1) + start = p; + while (*q && (*q)->number < p->number) + q = &(*q)->link; + assert(*q == 0 || (*q)->number != p->number); + p->link = *q; + *q = p; + return p; +} + +/* term - create a new terminal id with external symbol number esn */ +Term term(char *id, int esn) { + Term p = lookup(id), *q = &terms; + + if (p) + yyerror("redefinition of terminal `%s'\n", id); + else + p = install(id); + p->kind = TERM; + p->esn = esn; + p->arity = -1; + while (*q && (*q)->esn < p->esn) + q = &(*q)->link; + if (*q && (*q)->esn == p->esn) + yyerror("duplicate external symbol number `%s=%d'\n", + p->name, p->esn); + p->link = *q; + *q = p; + return p; +} + +/* tree - create & initialize a tree node with the given fields */ +Tree tree(char *id, Tree left, Tree right) { + Tree t = alloc(sizeof *t); + Term p = lookup(id); + int arity = 0; + + if (left && right) + arity = 2; + else if (left) + arity = 1; + if (p == NULL && arity > 0) { + yyerror("undefined terminal `%s'\n", id); + p = term(id, -1); + } else if (p == NULL && arity == 0) + p = (Term)nonterm(id); + else if (p && p->kind == NONTERM && arity > 0) { + yyerror("`%s' is a nonterminal\n", id); + p = term(id, -1); + } + if (p->kind == TERM && p->arity == -1) + p->arity = arity; + if (p->kind == TERM && arity != p->arity) + yyerror("inconsistent arity for terminal `%s'\n", id); + t->op = p; + t->nterms = p->kind == TERM; + if ((t->left = left) != NULL) + t->nterms += left->nterms; + if ((t->right = right) != NULL) + t->nterms += right->nterms; + return t; +} + +/* rule - create & initialize a rule with the given fields */ +Rule rule(char *id, Tree pattern, char *template, char *code) { + Rule r = alloc(sizeof *r), *q; + Term p = pattern->op; + char *end; + + r->lhs = nonterm(id); + r->packed = ++r->lhs->lhscount; + for (q = &r->lhs->rules; *q; q = &(*q)->decode) + ; + *q = r; + r->pattern = pattern; + r->ern = ++nrules; + r->template = template; + r->code = code; + r->cost = strtol(code, &end, 10); + if (*end) { + r->cost = -1; + r->code = stringf("(%s)", code); + } + if (p->kind == TERM) { + for (q = &p->rules; *q; q = &(*q)->next) + ; + *q = r; + } else if (pattern->left == NULL && pattern->right == NULL) { + Nonterm p = pattern->op; + r->chain = p->chain; + p->chain = r; + if (r->cost == -1) + yyerror("illegal nonconstant cost `%s'\n", code); + } + for (q = &rules; *q; q = &(*q)->link) + ; + r->link = *q; + *q = r; + return r; +} + +/* print - formatted output */ +static void print(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + for ( ; *fmt; fmt++) + if (*fmt == '%') + switch (*++fmt) { + case 'd': fprintf(outfp, "%d", va_arg(ap, int)); break; + case 's': fputs(va_arg(ap, char *), outfp); break; + case 'P': fprintf(outfp, "%s_", prefix); break; + case 'T': { + Tree t = va_arg(ap, Tree); + print("%S", t->op); + if (t->left && t->right) + print("(%T,%T)", t->left, t->right); + else if (t->left) + print("(%T)", t->left); + break; + } + case 'R': { + Rule r = va_arg(ap, Rule); + print("%S: %T", r->lhs, r->pattern); + break; + } + case 'S': fputs(va_arg(ap, Term)->name, outfp); break; + case '1': case '2': case '3': case '4': case '5': { + int n = *fmt - '0'; + while (n-- > 0) + putc('\t', outfp); + break; + } + default: putc(*fmt, outfp); break; + } + else + putc(*fmt, outfp); + va_end(ap); +} + +/* reach - mark all nonterminals in tree t as reachable */ +static void reach(Tree t) { + Nonterm p = t->op; + + if (p->kind == NONTERM) + if (!p->reached) + ckreach(p); + if (t->left) + reach(t->left); + if (t->right) + reach(t->right); +} + +/* ckreach - mark all nonterminals reachable from p */ +static void ckreach(Nonterm p) { + Rule r; + + p->reached = 1; + for (r = p->rules; r; r = r->decode) + reach(r->pattern); +} + +/* emitcase - emit one case in function state */ +static void emitcase(Term p, int ntnumber) { + Rule r; + + print("%1case %d: /* %S */\n", p->esn, p); + switch (p->arity) { + case 0: case -1: + break; + case 1: + print("%2%Plabel(LEFT_CHILD(a));\n"); + break; + case 2: + print("%2%Plabel(LEFT_CHILD(a));\n"); + print("%2%Plabel(RIGHT_CHILD(a));\n"); + break; + default: assert(0); + } + for (r = p->rules; r; r = r->next) { + char *indent = "\t\t\0"; + switch (p->arity) { + case 0: case -1: + print("%2/* %R */\n", r); + if (r->cost == -1) { + print("%2c = %s;\n", r->code); + emitrecord("\t\t", r, "c", 0); + } else + emitrecord("\t\t", r, r->code, 0); + break; + case 1: + if (r->pattern->nterms > 1) { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "LEFT_CHILD(a)", " "); + print("%2) {\n"); + indent = "\t\t\t"; + } else + print("%2/* %R */\n", r); + if (r->pattern->nterms == 2 && r->pattern->left + && r->pattern->right == NULL) + emitrecalc(indent, r->pattern->op, r->pattern->left->op); + print("%sc = ", indent); + emitcost(r->pattern->left, "LEFT_CHILD(a)"); + print("%s;\n", r->code); + emitrecord(indent, r, "c", 0); + if (indent[2]) + print("%2}\n"); + break; + case 2: + if (r->pattern->nterms > 1) { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "LEFT_CHILD(a)", + r->pattern->right->nterms ? " && " : " "); + emittest(r->pattern->right, "RIGHT_CHILD(a)", " "); + print("%2) {\n"); + indent = "\t\t\t"; + } else + print("%2/* %R */\n", r); + print("%sc = ", indent); + emitcost(r->pattern->left, "LEFT_CHILD(a)"); + emitcost(r->pattern->right, "RIGHT_CHILD(a)"); + print("%s;\n", r->code); + emitrecord(indent, r, "c", 0); + if (indent[2]) + print("%2}\n"); + break; + default: assert(0); + } + } + print("%2break;\n"); +} + +/* emitclosure - emit the closure functions */ +static void emitclosure(Nonterm nts) { + Nonterm p; + + for (p = nts; p; p = p->link) + if (p->chain) + print("static void %Pclosure_%S(NODEPTR_TYPE, int);\n", p); + print("\n"); + for (p = nts; p; p = p->link) + if (p->chain) { + Rule r; + print("static void %Pclosure_%S(NODEPTR_TYPE a, int c) {\n" +"%1struct %Pstate *p = STATE_LABEL(a);\n", p); + for (r = p->chain; r; r = r->chain) + emitrecord("\t", r, "c", r->cost); + print("}\n\n"); + } +} + +/* emitcost - emit cost computation for tree t */ +static void emitcost(Tree t, char *v) { + Nonterm p = t->op; + + if (p->kind == TERM) { + if (t->left) + emitcost(t->left, stringf("LEFT_CHILD(%s)", v)); + if (t->right) + emitcost(t->right, stringf("RIGHT_CHILD(%s)", v)); + } else + print("((struct %Pstate *)(%s->x.state))->cost[%P%S_NT] + ", v, p); +} + +/* emitdefs - emit nonterminal defines and data structures */ +static void emitdefs(Nonterm nts, int ntnumber) { + Nonterm p; + + for (p = nts; p; p = p->link) + print("#define %P%S_NT %d\n", p, p->number); + print("\n"); + print("static char *%Pntname[] = {\n%10,\n"); + for (p = nts; p; p = p->link) + print("%1\"%S\",\n", p); + print("%10\n};\n\n"); +} + +/* emitheader - emit initial definitions */ +static void emitheader(void) { + time_t timer = time(NULL); + + print("/*\ngenerated at %sby %s\n*/\n", ctime(&timer), rcsid); + print("static void %Pkids(NODEPTR_TYPE, int, NODEPTR_TYPE[]);\n"); + print("static void %Plabel(NODEPTR_TYPE);\n"); + print("static int %Prule(void*, int);\n\n"); +} + +/* computekids - compute paths to kids in tree t */ +static char *computekids(Tree t, char *v, char *bp, int *ip) { + Term p = t->op; + + if (p->kind == NONTERM) { + sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v); + bp += strlen(bp); + } else if (p->arity > 0) { + bp = computekids(t->left, stringf("LEFT_CHILD(%s)", v), bp, ip); + if (p->arity == 2) + bp = computekids(t->right, stringf("RIGHT_CHILD(%s)", v), bp, ip); + } + return bp; +} + +/* emitkids - emit _kids */ +static void emitkids(Rule rules, int nrules) { + int i; + Rule r, *rc = alloc((nrules + 1 + 1)*sizeof *rc); + char **str = alloc((nrules + 1 + 1)*sizeof *str); + + for (i = 0, r = rules; r; r = r->link) { + int j = 0; + char buf[1024], *bp = buf; + *computekids(r->pattern, "p", bp, &j) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) + str[j] = strcpy(alloc(strlen(buf) + 1), buf); + r->kids = rc[j]; + rc[j] = r; + } + print("static void %Pkids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]) {\n" +"%1if (!p)\n%2fatal(\"%Pkids\", \"Null tree\\n\", 0);\n" +"%1if (!kids)\n%2fatal(\"%Pkids\", \"Null kids\\n\", 0);\n" +"%1switch (eruleno) {\n"); + for (i = 0; (r = rc[i]) != NULL; i++) { + for ( ; r; r = r->kids) + print("%1case %d: /* %R */\n", r->ern, r); + print("%s%2break;\n", str[i]); + } + print("%1default:\n%2fatal(\"%Pkids\", \"Bad rule number %%d\\n\", eruleno);\n%1}\n}\n\n"); +} + +/* emitlabel - emit label function */ +static void emitlabel(Term terms, Nonterm start, int ntnumber) { + int i; + Term p; + + print("static void %Plabel(NODEPTR_TYPE a) {\n%1int c;\n" +"%1struct %Pstate *p;\n\n" +"%1if (!a)\n%2fatal(\"%Plabel\", \"Null tree\\n\", 0);\n"); + print("%1STATE_LABEL(a) = p = allocate(sizeof *p, FUNC);\n" +"%1p->rule._stmt = 0;\n"); + for (i = 1; i <= ntnumber; i++) + print("%1p->cost[%d] =\n", i); + print("%20x7fff;\n%1switch (OP_LABEL(a)) {\n"); + for (p = terms; p; p = p->link) + emitcase(p, ntnumber); + print("%1default:\n" +"%2fatal(\"%Plabel\", \"Bad terminal %%d\\n\", OP_LABEL(a));\n%1}\n}\n\n"); +} + +/* computents - fill in bp with _nts vector for tree t */ +static char *computents(Tree t, char *bp) { + if (t) { + Nonterm p = t->op; + if (p->kind == NONTERM) { + sprintf(bp, "%s_%s_NT, ", prefix, p->name); + bp += strlen(bp); + } else + bp = computents(t->right, computents(t->left, bp)); + } + return bp; +} + +/* emitnts - emit _nts ragged array */ +static void emitnts(Rule rules, int nrules) { + Rule r; + int i, j, *nts = alloc((nrules + 1)*sizeof *nts); + char **str = alloc((nrules + 1)*sizeof *str); + + for (i = 0, r = rules; r; r = r->link) { + char buf[1024]; + *computents(r->pattern, buf) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) { + print("static short %Pnts_%d[] = { %s0 };\n", j, buf); + str[j] = strcpy(alloc(strlen(buf) + 1), buf); + } + nts[i++] = j; + } + print("\nstatic short *%Pnts[] = {\n"); + for (i = j = 0, r = rules; r; r = r->link) { + for ( ; j < r->ern; j++) + print("%10,%1/* %d */\n", j); + print("%1%Pnts_%d,%1/* %d */\n", nts[i++], j++); + } + print("};\n\n"); +} + +/* emitrecalc - emit code that tests for recalculation of INDIR?(VREGP) */ +static void emitrecalc(char *pre, Term root, Term kid) { + if (root->kind == TERM && strncmp(root->name, "INDIR", 5) == 0 + && kid->kind == TERM && strcmp(kid->name, "VREGP" ) == 0) { + Nonterm p; + print("%sif (mayrecalc(a)) {\n", pre); + print("%s%1struct %Pstate *q = a->syms[RX]->u.t.cse->x.state;\n", pre); + for (p = nts; p; p = p->link) { + print("%s%1if (q->cost[%P%S_NT] == 0) {\n", pre, p); + print("%s%2p->cost[%P%S_NT] = 0;\n", pre, p); + print("%s%2p->rule.%P%S = q->rule.%P%S;\n", pre, p, p); + print("%s%1}\n", pre); + } + print("%s}\n", pre); + } +} + +/* emitrecord - emit code that tests for a winning match of rule r */ +static void emitrecord(char *pre, Rule r, char *c, int cost) { + if (Tflag) + print("%s%Ptrace(a, %d, %s + %d, p->cost[%P%S_NT]);\n", + pre, r->ern, c, cost, r->lhs); + print("%sif (", pre); + print("%s + %d < p->cost[%P%S_NT]) {\n" +"%s%1p->cost[%P%S_NT] = %s + %d;\n%s%1p->rule.%P%S = %d;\n", + c, cost, r->lhs, pre, r->lhs, c, cost, pre, r->lhs, + r->packed); + if (r->lhs->chain) + print("%s%1%Pclosure_%S(a, %s + %d);\n", pre, r->lhs, c, cost); + print("%s}\n", pre); +} + +/* emitrule - emit decoding vectors and _rule */ +static void emitrule(Nonterm nts) { + Nonterm p; + + for (p = nts; p; p = p->link) { + Rule r; + print("static short %Pdecode_%S[] = {\n%10,\n", p); + for (r = p->rules; r; r = r->decode) + print("%1%d,\n", r->ern); + print("};\n\n"); + } + print("static int %Prule(void *state, int goalnt) {\n" +"%1if (goalnt < 1 || goalnt > %d)\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n" +"%1if (!state)\n%2return 0;\n%1switch (goalnt) {\n", ntnumber); + for (p = nts; p; p = p->link) + print("%1case %P%S_NT:" +"%1return %Pdecode_%S[((struct %Pstate *)state)->rule.%P%S];\n", p, p, p); + print("%1default:\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n%2return 0;\n%1}\n}\n\n"); +} + +/* emitstring - emit arrays of templates, instruction flags, and rules */ +static void emitstring(Rule rules) { + Rule r; + + print("static char *%Ptemplates[] = {\n"); + print("/* 0 */%10,\n"); + for (r = rules; r; r = r->link) + print("/* %d */%1\"%s\",%1/* %R */\n", r->ern, r->template, r); + print("};\n"); + print("\nstatic char %Pisinstruction[] = {\n"); + print("/* 0 */%10,\n"); + for (r = rules; r; r = r->link) { + int len = strlen(r->template); + print("/* %d */%1%d,%1/* %s */\n", r->ern, + len >= 2 && r->template[len-2] == '\\' && r->template[len-1] == 'n', + r->template); + } + print("};\n"); + print("\nstatic char *%Pstring[] = {\n"); + print("/* 0 */%10,\n"); + for (r = rules; r; r = r->link) + print("/* %d */%1\"%R\",\n", r->ern, r); + print("};\n\n"); +} + +/* emitstruct - emit the definition of the state structure */ +static void emitstruct(Nonterm nts, int ntnumber) { + print("struct %Pstate {\n%1short cost[%d];\n%1struct {\n", ntnumber + 1); + for ( ; nts; nts = nts->link) { + int n = 1, m = nts->lhscount; + while ((m >>= 1) != 0) + n++; + print("%2unsigned int %P%S:%d;\n", nts, n); + } + print("%1} rule;\n};\n\n"); +} + +/* emittest - emit clause for testing a match */ +static void emittest(Tree t, char *v, char *suffix) { + Term p = t->op; + + if (p->kind == TERM) { + print("%3%s->op == %d%s/* %S */\n", v, p->esn, + t->nterms > 1 ? " && " : suffix, p); + if (t->left) + emittest(t->left, stringf("LEFT_CHILD(%s)", v), + t->right && t->right->nterms ? " && " : suffix); + if (t->right) + emittest(t->right, stringf("RIGHT_CHILD(%s)", v), suffix); + } +} diff --git a/src/tools/lcc/lburg/lburg.h b/src/tools/lcc/lburg/lburg.h new file mode 100644 index 0000000..b67e802 --- /dev/null +++ b/src/tools/lcc/lburg/lburg.h @@ -0,0 +1,65 @@ +#ifndef BURG_INCLUDED +#define BURG_INCLUDED + +/* iburg.c: */ +extern void *alloc(int nbytes); + +typedef enum { TERM=1, NONTERM } Kind; +typedef struct rule *Rule; +typedef struct term *Term; +struct term { /* terminals: */ + char *name; /* terminal name */ + Kind kind; /* TERM */ + int esn; /* external symbol number */ + int arity; /* operator arity */ + Term link; /* next terminal in esn order */ + Rule rules; /* rules whose pattern starts with term */ +}; + +typedef struct nonterm *Nonterm; +struct nonterm { /* nonterminals: */ + char *name; /* nonterminal name */ + Kind kind; /* NONTERM */ + int number; /* identifying number */ + int lhscount; /* # times nt appears in a rule lhs */ + int reached; /* 1 iff reached from start nonterminal */ + Rule rules; /* rules w/nonterminal on lhs */ + Rule chain; /* chain rules w/nonterminal on rhs */ + Nonterm link; /* next terminal in number order */ +}; +extern Nonterm nonterm(char *id); +extern Term term(char *id, int esn); + +typedef struct tree *Tree; +struct tree { /* tree patterns: */ + void *op; /* a terminal or nonterminal */ + Tree left, right; /* operands */ + int nterms; /* number of terminal nodes in this tree */ +}; +extern Tree tree(char *op, Tree left, Tree right); + +struct rule { /* rules: */ + Nonterm lhs; /* lefthand side nonterminal */ + Tree pattern; /* rule pattern */ + int ern; /* external rule number */ + int packed; /* packed external rule number */ + int cost; /* cost, if a constant */ + char *code; /* cost, if an expression */ + char *template; /* assembler template */ + Rule link; /* next rule in ern order */ + Rule next; /* next rule with same pattern root */ + Rule chain; /* next chain rule with same rhs */ + Rule decode; /* next rule with same lhs */ + Rule kids; /* next rule with same _kids pattern */ +}; +extern Rule rule(char *id, Tree pattern, char *template, char *code); + +/* gram.y: */ +void yyerror(char *fmt, ...); +int yyparse(void); +void yywarn(char *fmt, ...); +extern int errcnt; +extern FILE *infp; +extern FILE *outfp; + +#endif diff --git a/src/tools/lcc/src/alloc.c b/src/tools/lcc/src/alloc.c new file mode 100644 index 0000000..e0566df --- /dev/null +++ b/src/tools/lcc/src/alloc.c @@ -0,0 +1,94 @@ +#include "c.h" +struct block { + struct block *next; + char *limit; + char *avail; +}; +union align { + long l; + char *p; + double d; + int (*f)(void); +}; +union header { + struct block b; + union align a; +}; +#ifdef PURIFY +union header *arena[3]; + +void *allocate(unsigned long n, unsigned a) { + union header *new = malloc(sizeof *new + n); + + assert(a < NELEMS(arena)); + if (new == NULL) { + error("insufficient memory\n"); + exit(1); + } + new->b.next = (void *)arena[a]; + arena[a] = new; + return new + 1; +} + +void deallocate(unsigned a) { + union header *p, *q; + + assert(a < NELEMS(arena)); + for (p = arena[a]; p; p = q) { + q = (void *)p->b.next; + free(p); + } + arena[a] = NULL; +} + +void *newarray(unsigned long m, unsigned long n, unsigned a) { + return allocate(m*n, a); +} +#else +static struct block + first[] = { { NULL }, { NULL }, { NULL } }, + *arena[] = { &first[0], &first[1], &first[2] }; +static struct block *freeblocks; + +void *allocate(unsigned long n, unsigned a) { + struct block *ap; + + assert(a < NELEMS(arena)); + assert(n > 0); + ap = arena[a]; + n = roundup(n, sizeof (union align)); + while (n > ap->limit - ap->avail) { + if ((ap->next = freeblocks) != NULL) { + freeblocks = freeblocks->next; + ap = ap->next; + } else + { + unsigned m = sizeof (union header) + n + roundup(10*1024, sizeof (union align)); + ap->next = malloc(m); + ap = ap->next; + if (ap == NULL) { + error("insufficient memory\n"); + exit(1); + } + ap->limit = (char *)ap + m; + } + ap->avail = (char *)((union header *)ap + 1); + ap->next = NULL; + arena[a] = ap; + + } + ap->avail += n; + return ap->avail - n; +} + +void *newarray(unsigned long m, unsigned long n, unsigned a) { + return allocate(m*n, a); +} +void deallocate(unsigned a) { + assert(a < NELEMS(arena)); + arena[a]->next = freeblocks; + freeblocks = first[a].next; + first[a].next = NULL; + arena[a] = &first[a]; +} +#endif diff --git a/src/tools/lcc/src/bind.c b/src/tools/lcc/src/bind.c new file mode 100644 index 0000000..bc7b983 --- /dev/null +++ b/src/tools/lcc/src/bind.c @@ -0,0 +1,8 @@ +#include "c.h" +extern Interface nullIR; +extern Interface bytecodeIR; +Binding bindings[] = { + { "null", &nullIR }, + { "bytecode", &bytecodeIR }, + { NULL, NULL }, +}; diff --git a/src/tools/lcc/src/bytecode.c b/src/tools/lcc/src/bytecode.c new file mode 100644 index 0000000..e83429f --- /dev/null +++ b/src/tools/lcc/src/bytecode.c @@ -0,0 +1,367 @@ +#include "c.h" +#define I(f) b_##f + + +static void I(segment)(int n) { + static int cseg; + + if (cseg != n) + switch (cseg = n) { + case CODE: print("code\n"); return; + case DATA: print("data\n"); return; + case BSS: print("bss\n"); return; + case LIT: print("lit\n"); return; + default: assert(0); + } +} + +static void I(address)(Symbol q, Symbol p, long n) { + q->x.name = stringf("%s%s%D", p->x.name, n > 0 ? "+" : "", n); +} + +static void I(defaddress)(Symbol p) { + print("address %s\n", p->x.name); +} + +static void I(defconst)(int suffix, int size, Value v) { + switch (suffix) { + case I: + if (size > sizeof (int)) + print("byte %d %D\n", size, v.i); + else + print("byte %d %d\n", size, v.i); + return; + case U: + if (size > sizeof (unsigned)) + print("byte %d %U\n", size, v.u); + else + print("byte %d %u\n", size, v.u); + return; + case P: print("byte %d %U\n", size, (unsigned long)v.p); return; + case F: + if (size == 4) { + floatint_t fi; + fi.f = v.d; + print("byte 4 %u\n", fi.ui); + } else { + unsigned *p = (unsigned *)&v.d; + print("byte 4 %u\n", p[swap]); + print("byte 4 %u\n", p[1 - swap]); + } + return; + } + assert(0); +} + +static void I(defstring)(int len, char *str) { + char *s; + + for (s = str; s < str + len; s++) + print("byte 1 %d\n", (*s)&0377); +} + +static void I(defsymbol)(Symbol p) { + if (p->scope == CONSTANTS) + switch (optype(ttob(p->type))) { + case I: p->x.name = stringf("%D", p->u.c.v.i); break; + case U: p->x.name = stringf("%U", p->u.c.v.u); break; + case P: p->x.name = stringf("%U", p->u.c.v.p); break; + case F: + { // JDC: added this to get inline floats + floatint_t temp; + + temp.f = p->u.c.v.d; + p->x.name = stringf("%U", temp.ui ); + } + break;// JDC: added this + default: assert(0); + } + else if (p->scope >= LOCAL && p->sclass == STATIC) + p->x.name = stringf("$%d", genlabel(1)); + else if (p->scope == LABELS || p->generated) + p->x.name = stringf("$%s", p->name); + else + p->x.name = p->name; +} + +static void dumptree(Node p) { + switch (specific(p->op)) { + case ASGN+B: + assert(p->kids[0]); + assert(p->kids[1]); + assert(p->syms[0]); + dumptree(p->kids[0]); + dumptree(p->kids[1]); + print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.u); + return; + case RET+V: + assert(!p->kids[0]); + assert(!p->kids[1]); + print("%s\n", opname(p->op)); + return; + } + switch (generic(p->op)) { + case CNST: case ADDRG: case ADDRF: case ADDRL: case LABEL: + assert(!p->kids[0]); + assert(!p->kids[1]); + assert(p->syms[0] && p->syms[0]->x.name); + print("%s %s\n", opname(p->op), p->syms[0]->x.name); + return; + case CVF: case CVI: case CVP: case CVU: + assert(p->kids[0]); + assert(!p->kids[1]); + assert(p->syms[0]); + dumptree(p->kids[0]); + print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.i); + return; + case ARG: case BCOM: case NEG: case INDIR: case JUMP: case RET: + assert(p->kids[0]); + assert(!p->kids[1]); + dumptree(p->kids[0]); + print("%s\n", opname(p->op)); + return; + case CALL: + assert(p->kids[0]); + assert(!p->kids[1]); + assert(optype(p->op) != B); + dumptree(p->kids[0]); + print("%s\n", opname(p->op)); + if ( !p->count ) { printf("pop\n"); }; // JDC + return; + case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH: + case ADD: case SUB: case DIV: case MUL: case MOD: + assert(p->kids[0]); + assert(p->kids[1]); + dumptree(p->kids[0]); + dumptree(p->kids[1]); + print("%s\n", opname(p->op)); + return; + case EQ: case NE: case GT: case GE: case LE: case LT: + assert(p->kids[0]); + assert(p->kids[1]); + assert(p->syms[0]); + assert(p->syms[0]->x.name); + dumptree(p->kids[0]); + dumptree(p->kids[1]); + print("%s %s\n", opname(p->op), p->syms[0]->x.name); + return; + } + assert(0); +} + +static void I(emit)(Node p) { + for (; p; p = p->link) + dumptree(p); +} + +static void I(export)(Symbol p) { + print("export %s\n", p->x.name); +} + +static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) { + int i; + + (*IR->segment)(CODE); + offset = 0; + for (i = 0; caller[i] && callee[i]; i++) { + offset = roundup(offset, caller[i]->type->align); + caller[i]->x.name = callee[i]->x.name = stringf("%d", offset); + caller[i]->x.offset = callee[i]->x.offset = offset; + offset += caller[i]->type->size; + } + maxargoffset = maxoffset = argoffset = offset = 0; + gencode(caller, callee); + print("proc %s %d %d\n", f->x.name, maxoffset, maxargoffset); + emitcode(); + print("endproc %s %d %d\n", f->x.name, maxoffset, maxargoffset); + +} + +static void gen02(Node p) { + assert(p); + if (generic(p->op) == ARG) { + assert(p->syms[0]); + argoffset += (p->syms[0]->u.c.v.i < 4 ? 4 : p->syms[0]->u.c.v.i); + } else if (generic(p->op) == CALL) { + maxargoffset = (argoffset > maxargoffset ? argoffset : maxargoffset); + argoffset = 0; + } +} + +static void gen01(Node p) { + if (p) { + gen01(p->kids[0]); + gen01(p->kids[1]); + gen02(p); + } +} + +static Node I(gen)(Node p) { + Node q; + + assert(p); + for (q = p; q; q = q->link) + gen01(q); + return p; +} + +static void I(global)(Symbol p) { + print("align %d\n", p->type->align > 4 ? 4 : p->type->align); + print("LABELV %s\n", p->x.name); +} + +static void I(import)(Symbol p) { + print("import %s\n", p->x.name); +} + +static void I(local)(Symbol p) { + offset = roundup(offset, p->type->align); + p->x.name = stringf("%d", offset); + p->x.offset = offset; + offset += p->type->size; +} + +static void I(progbeg)(int argc, char *argv[]) {} + +static void I(progend)(void) {} + +static void I(space)(int n) { + print("skip %d\n", n); +} + +//======================================================== + +// JDC: hacked up to get interleaved source lines in asm code +static char *sourceFile; +static char *sourcePtr; +static int sourceLine; + +static int filelength( FILE *f ) { + int pos; + int end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + +static void LoadSourceFile( const char *filename ) { + FILE *f; + int length; + + f = fopen( filename, "r" ); + if ( !f ) { + print( ";couldn't open %s\n", filename ); + sourceFile = NULL; + return; + } + length = filelength( f ); + sourceFile = malloc( length + 1 ); + if ( sourceFile ) { + size_t size; + size = fread( sourceFile, length, 1, f ); + sourceFile[length] = 0; + } + + fclose( f ); + sourceLine = 1; + sourcePtr = sourceFile; +} + +static void PrintToSourceLine( int line ) { + int c; + + if ( !sourceFile ) { + return; + } + while ( sourceLine <= line ) { + int i; + + for ( i = 0 ; sourcePtr[i] && sourcePtr[i] != '\n' ; i++ ) { + } + c = sourcePtr[i]; + if ( c == '\n' ) { + sourcePtr[i] = 0; + } + print( ";%d:%s\n", sourceLine, sourcePtr ); + if ( c == 0 ) { + sourcePtr += i; // end of file + } else { + sourcePtr += i+1; + } + sourceLine++; + } +} + +static void I(stabline)(Coordinate *cp) { + static char *prevfile; + static int prevline; + + if (cp->file && (prevfile == NULL || strcmp(prevfile, cp->file) != 0)) { + print("file \"%s\"\n", prevfile = cp->file); + prevline = 0; + if ( sourceFile ) { + free( sourceFile ); + sourceFile = NULL; + } + // load the new source file + LoadSourceFile( cp->file ); + } + if (cp->y != prevline) { + print("line %d\n", prevline = cp->y); + PrintToSourceLine( cp->y ); + } +} + +//======================================================== + +#define b_blockbeg blockbeg +#define b_blockend blockend + +Interface bytecodeIR = { + {1, 1, 0}, /* char */ + {2, 2, 0}, /* short */ + {4, 4, 0}, /* int */ + {4, 4, 0}, /* long */ + {4, 4, 0}, /* long long */ + {4, 4, 0}, /* float */ // JDC: use inline floats + {4, 4, 0}, /* double */ // JDC: don't ever emit 8 byte double code + {4, 4, 0}, /* long double */ // JDC: don't ever emit 8 byte double code + {4, 4, 0}, /* T* */ + {0, 4, 0}, /* struct */ + 0, /* little_endian */ + 0, /* mulops_calls */ + 0, /* wants_callb */ + 0, /* wants_argb */ + 1, /* left_to_right */ + 0, /* wants_dag */ + 0, /* unsigned_char */ + I(address), + I(blockbeg), + I(blockend), + I(defaddress), + I(defconst), + I(defstring), + I(defsymbol), + I(emit), + I(export), + I(function), + I(gen), + I(global), + I(import), + I(local), + I(progbeg), + I(progend), + I(segment), + I(space), + 0, /* I(stabblock) */ + 0, /* I(stabend) */ + 0, /* I(stabfend) */ + 0, /* I(stabinit) */ + I(stabline), + 0, /* I(stabsym) */ + 0, /* I(stabtype) */ +}; diff --git a/src/tools/lcc/src/c.h b/src/tools/lcc/src/c.h new file mode 100644 index 0000000..68c8f62 --- /dev/null +++ b/src/tools/lcc/src/c.h @@ -0,0 +1,729 @@ +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> + +#define NEW(p,a) ((p) = allocate(sizeof *(p), (a))) +#define NEW0(p,a) memset(NEW((p),(a)), 0, sizeof *(p)) +#define isaddrop(op) (specific(op)==ADDRG+P || specific(op)==ADDRL+P \ + || specific(op)==ADDRF+P) + +#define MAXLINE 512 +#define BUFSIZE 4096 + +#define istypename(t,tsym) (kind[t] == CHAR \ + || (t == ID && tsym && tsym->sclass == TYPEDEF)) +#define sizeop(n) ((n)<<10) +#define generic(op) ((op)&0x3F0) +#define specific(op) ((op)&0x3FF) +#define opindex(op) (((op)>>4)&0x3F) +#define opkind(op) ((op)&~0x3F0) +#define opsize(op) ((op)>>10) +#define optype(op) ((op)&0xF) +#ifdef __LCC__ +#ifndef __STDC__ +#define __STDC__ +#endif +#endif +#define NELEMS(a) ((int)(sizeof (a)/sizeof ((a)[0]))) +#undef roundup +#define roundup(x,n) (((x)+((n)-1))&(~((n)-1))) +#define mkop(op,ty) (specific((op) + ttob(ty))) + +#define extend(x,ty) ((x)&(1<<(8*(ty)->size-1)) ? (x)|((~0UL)<<(8*(ty)->size-1)) : (x)&ones(8*(ty)->size)) +#define ones(n) ((n)>=8*sizeof (unsigned long) ? ~0UL : ~((~0UL)<<(n))) + +#define isqual(t) ((t)->op >= CONST) +#define unqual(t) (isqual(t) ? (t)->type : (t)) + +#define isvolatile(t) ((t)->op == VOLATILE \ + || (t)->op == CONST+VOLATILE) +#define isconst(t) ((t)->op == CONST \ + || (t)->op == CONST+VOLATILE) +#define isarray(t) (unqual(t)->op == ARRAY) +#define isstruct(t) (unqual(t)->op == STRUCT \ + || unqual(t)->op == UNION) +#define isunion(t) (unqual(t)->op == UNION) +#define isfunc(t) (unqual(t)->op == FUNCTION) +#define isptr(t) (unqual(t)->op == POINTER) +#define ischar(t) ((t)->size == 1 && isint(t)) +#define isint(t) (unqual(t)->op == INT \ + || unqual(t)->op == UNSIGNED) +#define isfloat(t) (unqual(t)->op == FLOAT) +#define isarith(t) (unqual(t)->op <= UNSIGNED) +#define isunsigned(t) (unqual(t)->op == UNSIGNED) +#define isscalar(t) (unqual(t)->op <= POINTER \ + || unqual(t)->op == ENUM) +#define isenum(t) (unqual(t)->op == ENUM) +#define fieldsize(p) (p)->bitsize +#define fieldright(p) ((p)->lsb - 1) +#define fieldleft(p) (8*(p)->type->size - \ + fieldsize(p) - fieldright(p)) +#define fieldmask(p) (~(~(unsigned)0<<fieldsize(p))) +typedef struct node *Node; + +typedef struct list *List; + +typedef struct code *Code; + +typedef struct swtch *Swtch; + +typedef struct symbol *Symbol; + +typedef struct coord { + char *file; + unsigned x, y; +} Coordinate; +typedef struct table *Table; + +typedef union value { + long i; + unsigned long u; + double d; + void *p; + void (*g)(void); +} Value; +typedef struct tree *Tree; + +typedef struct type *Type; + +typedef struct field *Field; + +typedef struct { + unsigned printed:1; + unsigned marked; + unsigned short typeno; + void *xt; +} Xtype; + +typedef union { + float f; + int i; + unsigned int ui; +} floatint_t; + +#include "config.h" +typedef struct metrics { + unsigned char size, align, outofline; +} Metrics; +typedef struct interface { + Metrics charmetric; + Metrics shortmetric; + Metrics intmetric; + Metrics longmetric; + Metrics longlongmetric; + Metrics floatmetric; + Metrics doublemetric; + Metrics longdoublemetric; + Metrics ptrmetric; + Metrics structmetric; + unsigned little_endian:1; + unsigned mulops_calls:1; + unsigned wants_callb:1; + unsigned wants_argb:1; + unsigned left_to_right:1; + unsigned wants_dag:1; + unsigned unsigned_char:1; +void (*address)(Symbol p, Symbol q, long n); +void (*blockbeg)(Env *); +void (*blockend)(Env *); +void (*defaddress)(Symbol); +void (*defconst) (int suffix, int size, Value v); +void (*defstring)(int n, char *s); +void (*defsymbol)(Symbol); +void (*emit) (Node); +void (*export)(Symbol); +void (*function)(Symbol, Symbol[], Symbol[], int); +Node (*gen) (Node); +void (*global)(Symbol); +void (*import)(Symbol); +void (*local)(Symbol); +void (*progbeg)(int argc, char *argv[]); +void (*progend)(void); +void (*segment)(int); +void (*space)(int); +void (*stabblock)(int, int, Symbol*); +void (*stabend) (Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *); +void (*stabfend) (Symbol, int); +void (*stabinit) (char *, int, char *[]); +void (*stabline) (Coordinate *); +void (*stabsym) (Symbol); +void (*stabtype) (Symbol); + Xinterface x; +} Interface; +typedef struct binding { + char *name; + Interface *ir; +} Binding; + +extern Binding bindings[]; +extern Interface *IR; +typedef struct { + List blockentry; + List blockexit; + List entry; + List exit; + List returns; + List points; + List calls; + List end; +} Events; + +enum { +#define xx(a,b,c,d,e,f,g) a=b, +#define yy(a,b,c,d,e,f,g) +#include "token.h" + LAST +}; +struct node { + short op; + short count; + Symbol syms[3]; + Node kids[2]; + Node link; + Xnode x; +}; +enum { + F=FLOAT, + I=INT, + U=UNSIGNED, + P=POINTER, + V=VOID, + B=STRUCT +}; +#define gop(name,value) name=value<<4, +#define op(name,type,sizes) + +enum { gop(CNST,1) + op(CNST,F,fdx) + op(CNST,I,csilh) + op(CNST,P,p) + op(CNST,U,csilh) + gop(ARG,2) + op(ARG,B,-) + op(ARG,F,fdx) + op(ARG,I,ilh) + op(ARG,P,p) + op(ARG,U,ilh) + gop(ASGN,3) + op(ASGN,B,-) + op(ASGN,F,fdx) + op(ASGN,I,csilh) + op(ASGN,P,p) + op(ASGN,U,csilh) + gop(INDIR,4) + op(INDIR,B,-) + op(INDIR,F,fdx) + op(INDIR,I,csilh) + op(INDIR,P,p) + op(INDIR,U,csilh) + gop(CVF,7) + op(CVF,F,fdx) + op(CVF,I,ilh) + gop(CVI,8) + op(CVI,F,fdx) + op(CVI,I,csilh) + op(CVI,U,csilhp) + gop(CVP,9) + op(CVP,U,p) + gop(CVU,11) + op(CVU,I,csilh) + op(CVU,P,p) + op(CVU,U,csilh) + gop(NEG,12) + op(NEG,F,fdx) + op(NEG,I,ilh) + gop(CALL,13) + op(CALL,B,-) + op(CALL,F,fdx) + op(CALL,I,ilh) + op(CALL,P,p) + op(CALL,U,ilh) + op(CALL,V,-) + gop(RET,15) + op(RET,F,fdx) + op(RET,I,ilh) + op(RET,P,p) + op(RET,U,ilh) + op(RET,V,-) + gop(ADDRG,16) + op(ADDRG,P,p) + gop(ADDRF,17) + op(ADDRF,P,p) + gop(ADDRL,18) + op(ADDRL,P,p) + gop(ADD,19) + op(ADD,F,fdx) + op(ADD,I,ilh) + op(ADD,P,p) + op(ADD,U,ilhp) + gop(SUB,20) + op(SUB,F,fdx) + op(SUB,I,ilh) + op(SUB,P,p) + op(SUB,U,ilhp) + gop(LSH,21) + op(LSH,I,ilh) + op(LSH,U,ilh) + gop(MOD,22) + op(MOD,I,ilh) + op(MOD,U,ilh) + gop(RSH,23) + op(RSH,I,ilh) + op(RSH,U,ilh) + gop(BAND,24) + op(BAND,I,ilh) + op(BAND,U,ilh) + gop(BCOM,25) + op(BCOM,I,ilh) + op(BCOM,U,ilh) + gop(BOR,26) + op(BOR,I,ilh) + op(BOR,U,ilh) + gop(BXOR,27) + op(BXOR,I,ilh) + op(BXOR,U,ilh) + gop(DIV,28) + op(DIV,F,fdx) + op(DIV,I,ilh) + op(DIV,U,ilh) + gop(MUL,29) + op(MUL,F,fdx) + op(MUL,I,ilh) + op(MUL,U,ilh) + gop(EQ,30) + op(EQ,F,fdx) + op(EQ,I,ilh) + op(EQ,U,ilhp) + gop(GE,31) + op(GE,F,fdx) + op(GE,I,ilh) + op(GE,U,ilhp) + gop(GT,32) + op(GT,F,fdx) + op(GT,I,ilh) + op(GT,U,ilhp) + gop(LE,33) + op(LE,F,fdx) + op(LE,I,ilh) + op(LE,U,ilhp) + gop(LT,34) + op(LT,F,fdx) + op(LT,I,ilh) + op(LT,U,ilhp) + gop(NE,35) + op(NE,F,fdx) + op(NE,I,ilh) + op(NE,U,ilhp) + gop(JUMP,36) + op(JUMP,V,-) + gop(LABEL,37) + op(LABEL,V,-) + gop(LOAD,14) + op(LOAD,B,-) + op(LOAD,F,fdx) + op(LOAD,I,csilh) + op(LOAD,P,p) + op(LOAD,U,csilhp) LASTOP }; + +#undef gop +#undef op +enum { CODE=1, BSS, DATA, LIT }; +enum { PERM=0, FUNC, STMT }; +struct list { + void *x; + List link; +}; + +struct code { + enum { Blockbeg, Blockend, Local, Address, Defpoint, + Label, Start, Gen, Jump, Switch + } kind; + Code prev, next; + union { + struct { + int level; + Symbol *locals; + Table identifiers, types; + Env x; + } block; + Code begin; + Symbol var; + + struct { + Symbol sym; + Symbol base; + long offset; + } addr; + struct { + Coordinate src; + int point; + } point; + Node forest; + struct { + Symbol sym; + Symbol table; + Symbol deflab; + int size; + long *values; + Symbol *labels; + } swtch; + + } u; +}; +struct swtch { + Symbol sym; + int lab; + Symbol deflab; + int ncases; + int size; + long *values; + Symbol *labels; +}; +struct symbol { + char *name; + int scope; + Coordinate src; + Symbol up; + List uses; + int sclass; + unsigned structarg:1; + + unsigned addressed:1; + unsigned computed:1; + unsigned temporary:1; + unsigned generated:1; + unsigned defined:1; + Type type; + float ref; + union { + struct { + int label; + Symbol equatedto; + } l; + struct { + unsigned cfields:1; + unsigned vfields:1; + Table ftab; /* omit */ + Field flist; + } s; + int value; + Symbol *idlist; + struct { + Value min, max; + } limits; + struct { + Value v; + Symbol loc; + } c; + struct { + Coordinate pt; + int label; + int ncalls; + Symbol *callee; + } f; + int seg; + Symbol alias; + struct { + Node cse; + int replace; + Symbol next; + } t; + } u; + Xsymbol x; +}; +enum { CONSTANTS=1, LABELS, GLOBAL, PARAM, LOCAL }; +struct tree { + int op; + Type type; + Tree kids[2]; + Node node; + union { + Value v; + Symbol sym; + + Field field; + } u; +}; +enum { + AND=38<<4, + NOT=39<<4, + OR=40<<4, + COND=41<<4, + RIGHT=42<<4, + FIELD=43<<4 +}; +struct type { + int op; + Type type; + int align; + int size; + union { + Symbol sym; + struct { + unsigned oldstyle:1; + Type *proto; + } f; + } u; + Xtype x; +}; +struct field { + char *name; + Type type; + int offset; + short bitsize; + short lsb; + Field link; +}; +extern int assignargs; +extern int prunetemps; +extern int nodecount; +extern Symbol cfunc; +extern Symbol retv; +extern Tree (*optree[])(int, Tree, Tree); + +extern char kind[]; +extern int errcnt; +extern int errlimit; +extern int wflag; +extern Events events; +extern float refinc; + +extern unsigned char *cp; +extern unsigned char *limit; +extern char *firstfile; +extern char *file; +extern char *line; +extern int lineno; +extern int t; +extern char *token; +extern Symbol tsym; +extern Coordinate src; +extern int Aflag; +extern int Pflag; +extern Symbol YYnull; +extern Symbol YYcheck; +extern int glevel; +extern int xref; + +extern int ncalled; +extern int npoints; + +extern int needconst; +extern int explicitCast; +extern struct code codehead; +extern Code codelist; +extern Table stmtlabs; +extern float density; +extern Table constants; +extern Table externals; +extern Table globals; +extern Table identifiers; +extern Table labels; +extern Table types; +extern int level; + +extern List loci, symbols; + +extern List symbols; + +extern int where; +extern Type chartype; +extern Type doubletype; +extern Type floattype; +extern Type inttype; +extern Type longdouble; +extern Type longtype; +extern Type longlong; +extern Type shorttype; +extern Type signedchar; +extern Type unsignedchar; +extern Type unsignedlonglong; +extern Type unsignedlong; +extern Type unsignedshort; +extern Type unsignedtype; +extern Type charptype; +extern Type funcptype; +extern Type voidptype; +extern Type voidtype; +extern Type unsignedptr; +extern Type signedptr; +extern Type widechar; +extern void *allocate(unsigned long n, unsigned a); +extern void deallocate(unsigned a); +extern void *newarray(unsigned long m, unsigned long n, unsigned a); +extern void walk(Tree e, int tlab, int flab); +extern Node listnodes(Tree e, int tlab, int flab); +extern Node newnode(int op, Node left, Node right, Symbol p); +extern Tree cvtconst(Tree); +extern void printdag(Node, int); +extern void compound(int, Swtch, int); +extern void defglobal(Symbol, int); +extern void finalize(void); +extern void program(void); + +extern Tree vcall(Symbol func, Type ty, ...); +extern Tree addrof(Tree); +extern Tree asgn(Symbol, Tree); +extern Tree asgntree(int, Tree, Tree); +extern Type assign(Type, Tree); +extern Tree bittree(int, Tree, Tree); +extern Tree call(Tree, Type, Coordinate); +extern Tree calltree(Tree, Type, Tree, Symbol); +extern Tree condtree(Tree, Tree, Tree); +extern Tree cnsttree(Type, ...); +extern Tree consttree(unsigned int, Type); +extern Tree eqtree(int, Tree, Tree); +extern int iscallb(Tree); +extern Tree shtree(int, Tree, Tree); +extern void typeerror(int, Tree, Tree); + +extern void test(int tok, char set[]); +extern void expect(int tok); +extern void skipto(int tok, char set[]); +extern void error(const char *, ...); +extern int fatal(const char *, const char *, int); +extern void warning(const char *, ...); + +typedef void (*Apply)(void *, void *, void *); +extern void attach(Apply, void *, List *); +extern void apply(List event, void *arg1, void *arg2); +extern Tree retype(Tree p, Type ty); +extern Tree rightkid(Tree p); +extern int hascall(Tree p); +extern Type binary(Type, Type); +extern Tree cast(Tree, Type); +extern Tree cond(Tree); +extern Tree expr0(int); +extern Tree expr(int); +extern Tree expr1(int); +extern Tree field(Tree, const char *); +extern char *funcname(Tree); +extern Tree idtree(Symbol); +extern Tree incr(int, Tree, Tree); +extern Tree lvalue(Tree); +extern Tree nullcall(Type, Symbol, Tree, Tree); +extern Tree pointer(Tree); +extern Tree rvalue(Tree); +extern Tree value(Tree); + +extern void defpointer(Symbol); +extern Type initializer(Type, int); +extern void swtoseg(int); + +extern void input_init(int, char *[]); +extern void fillbuf(void); +extern void nextline(void); + +extern int getchr(void); +extern int gettok(void); + +extern void emitcode(void); +extern void gencode (Symbol[], Symbol[]); +extern void fprint(FILE *f, const char *fmt, ...); +extern char *stringf(const char *, ...); +extern void check(Node); +extern void print(const char *, ...); + +extern List append(void *x, List list); +extern int length(List list); +extern void *ltov (List *list, unsigned a); +extern void init(int, char *[]); + +extern Type typename(void); +extern void checklab(Symbol p, void *cl); +extern Type enumdcl(void); +extern void main_init(int, char *[]); +extern int main(int, char *[]); + +extern void vfprint(FILE *, char *, const char *, va_list); + +extern int process(char *); +extern int findfunc(char *, char *); +extern int findcount(char *, int, int); + +extern Tree constexpr(int); +extern int intexpr(int, int); +extern Tree simplify(int, Type, Tree, Tree); +extern int ispow2(unsigned long u); + +extern int reachable(int); + +extern void addlocal(Symbol); +extern void branch(int); +extern Code code(int); +extern void definelab(int); +extern void definept(Coordinate *); +extern void equatelab(Symbol, Symbol); +extern Node jump(int); +extern void retcode(Tree); +extern void statement(int, Swtch, int); +extern void swcode(Swtch, int *, int, int); +extern void swgen(Swtch); + +extern char * string(const char *str); +extern char *stringn(const char *str, int len); +extern char *stringd(long n); +extern Symbol relocate(const char *name, Table src, Table dst); +extern void use(Symbol p, Coordinate src); +extern void locus(Table tp, Coordinate *cp); +extern Symbol allsymbols(Table); + +extern Symbol constant(Type, Value); +extern void enterscope(void); +extern void exitscope(void); +extern Symbol findlabel(int); +extern Symbol findtype(Type); +extern void foreach(Table, int, void (*)(Symbol, void *), void *); +extern Symbol genident(int, Type, int); +extern int genlabel(int); +extern Symbol install(const char *, Table *, int, int); +extern Symbol intconst(int); +extern Symbol lookup(const char *, Table); +extern Symbol mkstr(char *); +extern Symbol mksymbol(int, const char *, Type); +extern Symbol newtemp(int, int, int); +extern Table table(Table, int); +extern Symbol temporary(int, Type); +extern char *vtoa(Type, Value); + +extern int nodeid(Tree); +extern char *opname(int); +extern int *printed(int); +extern void printtree(Tree, int); +extern Tree root(Tree); +extern Tree texpr(Tree (*)(int), int, int); +extern Tree tree(int, Type, Tree, Tree); + +extern void type_init(int, char *[]); + +extern Type signedint(Type); + +extern int hasproto(Type); +extern void outtype(Type, FILE *); +extern void printdecl (Symbol p, Type ty); +extern void printproto(Symbol p, Symbol args[]); +extern char *typestring(Type ty, char *id); +extern Field fieldref(const char *name, Type ty); +extern Type array(Type, int, int); +extern Type atop(Type); +extern Type btot(int, int); +extern Type compose(Type, Type); +extern Type deref(Type); +extern int eqtype(Type, Type, int); +extern Field fieldlist(Type); +extern Type freturn(Type); +extern Type ftype(Type, Type); +extern Type func(Type, Type *, int); +extern Field newfield(char *, Type, Type); +extern Type newstruct(int, char *); +extern void printtype(Type, int); +extern Type promote(Type); +extern Type ptr(Type); +extern Type qual(int, Type); +extern void rmtypes(int); +extern int ttob(Type); +extern int variadic(Type); + diff --git a/src/tools/lcc/src/config.h b/src/tools/lcc/src/config.h new file mode 100644 index 0000000..6f0d5a6 --- /dev/null +++ b/src/tools/lcc/src/config.h @@ -0,0 +1,102 @@ +typedef struct { + unsigned char max_unaligned_load; + Symbol (*rmap)(int); + + void (*blkfetch)(int size, int off, int reg, int tmp); + void (*blkstore)(int size, int off, int reg, int tmp); + void (*blkloop)(int dreg, int doff, + int sreg, int soff, + int size, int tmps[]); + void (*_label)(Node); + int (*_rule)(void*, int); + short **_nts; + void (*_kids)(Node, int, Node*); + char **_string; + char **_templates; + char *_isinstruction; + char **_ntname; + void (*emit2)(Node); + void (*doarg)(Node); + void (*target)(Node); + void (*clobber)(Node); +} Xinterface; +extern int askregvar(Symbol, Symbol); +extern void blkcopy(int, int, int, int, int, int[]); +extern int getregnum(Node); +extern int mayrecalc(Node); +extern int mkactual(int, int); +extern void mkauto(Symbol); +extern Symbol mkreg(char *, int, int, int); +extern Symbol mkwildcard(Symbol *); +extern int move(Node); +extern int notarget(Node); +extern void parseflags(int, char **); +extern int range(Node, int, int); +extern unsigned regloc(Symbol); /* omit */ +extern void rtarget(Node, int, Symbol); +extern void setreg(Node, Symbol); +extern void spill(unsigned, int, Node); +extern int widens(Node); + +extern int argoffset, maxargoffset; +extern int bflag, dflag; +extern int dalign, salign; +extern int framesize; +extern unsigned freemask[], usedmask[]; +extern int offset, maxoffset; +extern int swap; +extern unsigned tmask[], vmask[]; +typedef struct { + unsigned listed:1; + unsigned registered:1; + unsigned emitted:1; + unsigned copy:1; + unsigned equatable:1; + unsigned spills:1; + unsigned mayrecalc:1; + void *state; + short inst; + Node kids[3]; + Node prev, next; + Node prevuse; + short argno; +} Xnode; +typedef struct { + Symbol vbl; + short set; + short number; + unsigned mask; +} *Regnode; +enum { IREG=0, FREG=1 }; +typedef struct { + char *name; + unsigned int eaddr; /* omit */ + int offset; + Node lastuse; + int usecount; + Regnode regnode; + Symbol *wildcard; +} Xsymbol; +enum { RX=2 }; +typedef struct { + int offset; + unsigned freemask[2]; +} Env; + +#define LBURG_MAX SHRT_MAX + +enum { VREG=(44<<4) }; + +/* Exported for the front end */ +extern void blockbeg(Env *); +extern void blockend(Env *); +extern void emit(Node); +extern Node gen(Node); + +extern unsigned emitbin(Node, int); + +#ifdef NDEBUG +#define debug(x) (void)0 +#else +#define debug(x) (void)(dflag&&((x),0)) +#endif diff --git a/src/tools/lcc/src/dag.c b/src/tools/lcc/src/dag.c new file mode 100644 index 0000000..420cbe7 --- /dev/null +++ b/src/tools/lcc/src/dag.c @@ -0,0 +1,736 @@ +#include "c.h" + + +#define iscall(op) (generic(op) == CALL \ + || (IR->mulops_calls \ + && (generic(op)==DIV||generic(op)==MOD||generic(op)==MUL) \ + && ( optype(op)==U || optype(op)==I))) +static Node forest; +static struct dag { + struct node node; + struct dag *hlink; +} *buckets[16]; +int nodecount; +static Tree firstarg; +int assignargs = 1; +int prunetemps = -1; +static Node *tail; + +static int depth = 0; +static Node replace(Node); +static Node prune(Node); +static Node asgnnode(Symbol, Node); +static struct dag *dagnode(int, Node, Node, Symbol); +static Symbol equated(Symbol); +static void fixup(Node); +static void labelnode(int); +static void list(Node); +static void kill(Symbol); +static Node node(int, Node, Node, Symbol); +static void printdag1(Node, int, int); +static void printnode(Node, int, int); +static void reset(void); +static Node tmpnode(Node); +static void typestab(Symbol, void *); +static Node undag(Node); +static Node visit(Node, int); +static void unlist(void); +void walk(Tree tp, int tlab, int flab) { + listnodes(tp, tlab, flab); + if (forest) { + Node list = forest->link; + forest->link = NULL; + if (!IR->wants_dag) + list = undag(list); + code(Gen)->u.forest = list; + forest = NULL; + } + reset(); + deallocate(STMT); +} + +static Node node(int op, Node l, Node r, Symbol sym) { + int i; + struct dag *p; + + i = (opindex(op)^((unsigned long)sym>>2))&(NELEMS(buckets)-1); + for (p = buckets[i]; p; p = p->hlink) + if (p->node.op == op && p->node.syms[0] == sym + && p->node.kids[0] == l && p->node.kids[1] == r) + return &p->node; + p = dagnode(op, l, r, sym); + p->hlink = buckets[i]; + buckets[i] = p; + ++nodecount; + return &p->node; +} +static struct dag *dagnode(int op, Node l, Node r, Symbol sym) { + struct dag *p; + + NEW0(p, FUNC); + p->node.op = op; + if ((p->node.kids[0] = l) != NULL) + ++l->count; + if ((p->node.kids[1] = r) != NULL) + ++r->count; + p->node.syms[0] = sym; + return p; +} +Node newnode(int op, Node l, Node r, Symbol sym) { + return &dagnode(op, l, r, sym)->node; +} +static void kill(Symbol p) { + int i; + struct dag **q; + + for (i = 0; i < NELEMS(buckets); i++) + for (q = &buckets[i]; *q; ) + if (generic((*q)->node.op) == INDIR && + (!isaddrop((*q)->node.kids[0]->op) + || (*q)->node.kids[0]->syms[0] == p)) { + *q = (*q)->hlink; + --nodecount; + } else + q = &(*q)->hlink; +} +static void reset(void) { + if (nodecount > 0) + memset(buckets, 0, sizeof buckets); + nodecount = 0; +} +Node listnodes(Tree tp, int tlab, int flab) { + Node p = NULL, l, r; + int op; + + assert(tlab || flab || (tlab == 0 && flab == 0)); + if (tp == NULL) + return NULL; + if (tp->node) + return tp->node; + op = tp->op + sizeop(tp->type->size); + switch (generic(tp->op)) { + case AND: { if (depth++ == 0) reset(); + if (flab) { + listnodes(tp->kids[0], 0, flab); + listnodes(tp->kids[1], 0, flab); + } else { + listnodes(tp->kids[0], 0, flab = genlabel(1)); + listnodes(tp->kids[1], tlab, 0); + labelnode(flab); + } + depth--; } break; + case OR: { if (depth++ == 0) + reset(); + if (tlab) { + listnodes(tp->kids[0], tlab, 0); + listnodes(tp->kids[1], tlab, 0); + } else { + tlab = genlabel(1); + listnodes(tp->kids[0], tlab, 0); + listnodes(tp->kids[1], 0, flab); + labelnode(tlab); + } + depth--; + } break; + case NOT: { return listnodes(tp->kids[0], flab, tlab); } + case COND: { Tree q = tp->kids[1]; + assert(tlab == 0 && flab == 0); + if (tp->u.sym) + addlocal(tp->u.sym); + flab = genlabel(2); + listnodes(tp->kids[0], 0, flab); + assert(q && q->op == RIGHT); + reset(); + listnodes(q->kids[0], 0, 0); + if (forest->op == LABEL+V) { + equatelab(forest->syms[0], findlabel(flab + 1)); + unlist(); + } + list(jump(flab + 1)); + labelnode(flab); + listnodes(q->kids[1], 0, 0); + if (forest->op == LABEL+V) { + equatelab(forest->syms[0], findlabel(flab + 1)); + unlist(); + } + labelnode(flab + 1); + + if (tp->u.sym) + p = listnodes(idtree(tp->u.sym), 0, 0); } break; + case CNST: { Type ty = unqual(tp->type); + assert(ty->u.sym); + if (tlab || flab) { + assert(ty == inttype); + if (tlab && tp->u.v.i != 0) + list(jump(tlab)); + else if (flab && tp->u.v.i == 0) + list(jump(flab)); + } + else if (ty->u.sym->addressed) + p = listnodes(cvtconst(tp), 0, 0); + else + p = node(op, NULL, NULL, constant(ty, tp->u.v)); } break; + case RIGHT: { if ( tp->kids[0] && tp->kids[1] + && generic(tp->kids[1]->op) == ASGN + && ((generic(tp->kids[0]->op) == INDIR + && tp->kids[0]->kids[0] == tp->kids[1]->kids[0]) + || (tp->kids[0]->op == FIELD + && tp->kids[0] == tp->kids[1]->kids[0]))) { + assert(tlab == 0 && flab == 0); + if (generic(tp->kids[0]->op) == INDIR) { + p = listnodes(tp->kids[0], 0, 0); + list(p); + listnodes(tp->kids[1], 0, 0); + } + else { + assert(generic(tp->kids[0]->kids[0]->op) == INDIR); + list(listnodes(tp->kids[0]->kids[0], 0, 0)); + p = listnodes(tp->kids[0], 0, 0); + listnodes(tp->kids[1], 0, 0); + } + } else if (tp->kids[1]) { + listnodes(tp->kids[0], 0, 0); + p = listnodes(tp->kids[1], tlab, flab); + } else + p = listnodes(tp->kids[0], tlab, flab); } break; + case JUMP: { assert(tlab == 0 && flab == 0); + assert(tp->u.sym == 0); + assert(tp->kids[0]); + l = listnodes(tp->kids[0], 0, 0); + list(newnode(JUMP+V, l, NULL, NULL)); + reset(); } break; + case CALL: { Tree save = firstarg; + firstarg = NULL; + assert(tlab == 0 && flab == 0); + if (tp->op == CALL+B && !IR->wants_callb) { + Tree arg0 = tree(ARG+P, tp->kids[1]->type, + tp->kids[1], NULL); + if (IR->left_to_right) + firstarg = arg0; + l = listnodes(tp->kids[0], 0, 0); + if (!IR->left_to_right || firstarg) { + firstarg = NULL; + listnodes(arg0, 0, 0); + } + p = newnode(CALL+V, l, NULL, NULL); + } else { + l = listnodes(tp->kids[0], 0, 0); + r = listnodes(tp->kids[1], 0, 0); + p = newnode(tp->op == CALL+B ? tp->op : op, l, r, NULL); + } + NEW0(p->syms[0], FUNC); + assert(isptr(tp->kids[0]->type)); + assert(isfunc(tp->kids[0]->type->type)); + p->syms[0]->type = tp->kids[0]->type->type; + list(p); + reset(); + cfunc->u.f.ncalls++; + firstarg = save; + } break; + case ARG: { assert(tlab == 0 && flab == 0); + if (IR->left_to_right) + listnodes(tp->kids[1], 0, 0); + if (firstarg) { + Tree arg = firstarg; + firstarg = NULL; + listnodes(arg, 0, 0); + } + l = listnodes(tp->kids[0], 0, 0); + list(newnode(tp->op == ARG+B ? tp->op : op, l, NULL, NULL)); + forest->syms[0] = intconst(tp->type->size); + forest->syms[1] = intconst(tp->type->align); + if (!IR->left_to_right) + listnodes(tp->kids[1], 0, 0); } break; + case EQ: case NE: case GT: case GE: case LE: + case LT: { assert(tp->u.sym == 0); + assert(errcnt || tlab || flab); + l = listnodes(tp->kids[0], 0, 0); + r = listnodes(tp->kids[1], 0, 0); + assert(errcnt || opkind(l->op) == opkind(r->op)); + assert(errcnt || optype(op) == optype(l->op)); + if (tlab) + assert(flab == 0), + list(newnode(generic(tp->op) + opkind(l->op), l, r, findlabel(tlab))); + else if (flab) { + switch (generic(tp->op)) { + case EQ: op = NE; break; + case NE: op = EQ; break; + case GT: op = LE; break; + case LT: op = GE; break; + case GE: op = LT; break; + case LE: op = GT; break; + default: assert(0); + } + list(newnode(op + opkind(l->op), l, r, findlabel(flab))); + } + if (forest && forest->syms[0]) + forest->syms[0]->ref++; } break; + case ASGN: { assert(tlab == 0 && flab == 0); + if (tp->kids[0]->op == FIELD) { + Tree x = tp->kids[0]->kids[0]; + Field f = tp->kids[0]->u.field; + assert(generic(x->op) == INDIR); + reset(); + l = listnodes(lvalue(x), 0, 0); + if (fieldsize(f) < 8*f->type->size) { + unsigned int fmask = fieldmask(f); + unsigned int mask = fmask<<fieldright(f); + Tree q = tp->kids[1]; + if ((q->op == CNST+I && q->u.v.i == 0) + || (q->op == CNST+U && q->u.v.u == 0)) + q = bittree(BAND, x, cnsttree(unsignedtype, (unsigned long)~mask)); + else if ((q->op == CNST+I && (q->u.v.i&fmask) == fmask) + || (q->op == CNST+U && (q->u.v.u&fmask) == fmask)) + q = bittree(BOR, x, cnsttree(unsignedtype, (unsigned long)mask)); + else { + listnodes(q, 0, 0); + q = bittree(BOR, + bittree(BAND, rvalue(lvalue(x)), + cnsttree(unsignedtype, (unsigned long)~mask)), + bittree(BAND, shtree(LSH, cast(q, unsignedtype), + cnsttree(unsignedtype, (unsigned long)fieldright(f))), + cnsttree(unsignedtype, (unsigned long)mask))); + } + r = listnodes(q, 0, 0); + op = ASGN + ttob(q->type); + } else { + r = listnodes(tp->kids[1], 0, 0); + op = ASGN + ttob(tp->kids[1]->type); + } + } else { + l = listnodes(tp->kids[0], 0, 0); + r = listnodes(tp->kids[1], 0, 0); + } + list(newnode(tp->op == ASGN+B ? tp->op : op, l, r, NULL)); + forest->syms[0] = intconst(tp->kids[1]->type->size); + forest->syms[1] = intconst(tp->kids[1]->type->align); + if (isaddrop(tp->kids[0]->op) + && !tp->kids[0]->u.sym->computed) + kill(tp->kids[0]->u.sym); + else + reset(); + p = listnodes(tp->kids[1], 0, 0); } break; + case BOR: case BAND: case BXOR: + case ADD: case SUB: case RSH: + case LSH: { assert(tlab == 0 && flab == 0); + l = listnodes(tp->kids[0], 0, 0); + r = listnodes(tp->kids[1], 0, 0); + p = node(op, l, r, NULL); } break; + case DIV: case MUL: + case MOD: { assert(tlab == 0 && flab == 0); + l = listnodes(tp->kids[0], 0, 0); + r = listnodes(tp->kids[1], 0, 0); + p = node(op, l, r, NULL); + if (IR->mulops_calls && isint(tp->type)) { + list(p); + cfunc->u.f.ncalls++; + } } break; + case RET: { assert(tlab == 0 && flab == 0); + l = listnodes(tp->kids[0], 0, 0); + list(newnode(op, l, NULL, NULL)); } break; + case CVF: case CVI: case CVP: + case CVU: { assert(tlab == 0 && flab == 0); + assert(optype(tp->kids[0]->op) != optype(tp->op) || tp->kids[0]->type->size != tp->type->size); + l = listnodes(tp->kids[0], 0, 0); + p = node(op, l, NULL, intconst(tp->kids[0]->type->size)); + } break; + case BCOM: + case NEG: { assert(tlab == 0 && flab == 0); + l = listnodes(tp->kids[0], 0, 0); + p = node(op, l, NULL, NULL); } break; + case INDIR: { Type ty = tp->kids[0]->type; + assert(tlab == 0 && flab == 0); + l = listnodes(tp->kids[0], 0, 0); + if (isptr(ty)) + ty = unqual(ty)->type; + if (isvolatile(ty) + || (isstruct(ty) && unqual(ty)->u.sym->u.s.vfields)) + p = newnode(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL); + else + p = node(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL); } break; + case FIELD: { Tree q = tp->kids[0]; + if (tp->type == inttype) { + long n = fieldleft(tp->u.field); + q = shtree(RSH, + shtree(LSH, q, cnsttree(inttype, n)), + cnsttree(inttype, n + fieldright(tp->u.field))); + } else if (fieldsize(tp->u.field) < 8*tp->u.field->type->size) + q = bittree(BAND, + shtree(RSH, q, cnsttree(inttype, (long)fieldright(tp->u.field))), + cnsttree(unsignedtype, (unsigned long)fieldmask(tp->u.field))); + assert(tlab == 0 && flab == 0); + p = listnodes(q, 0, 0); } break; + case ADDRG: + case ADDRF: { assert(tlab == 0 && flab == 0); + p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym); + } break; + case ADDRL: { assert(tlab == 0 && flab == 0); + if (tp->u.sym->temporary) + addlocal(tp->u.sym); + p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym); } break; + default:assert(0); + } + tp->node = p; + return p; +} +static void list(Node p) { + if (p && p->link == NULL) { + if (forest) { + p->link = forest->link; + forest->link = p; + } else + p->link = p; + forest = p; + } +} +static void labelnode(int lab) { + assert(lab); + if (forest && forest->op == LABEL+V) + equatelab(findlabel(lab), forest->syms[0]); + else + list(newnode(LABEL+V, NULL, NULL, findlabel(lab))); + reset(); +} +static void unlist(void) { + Node p; + + assert(forest); + assert(forest != forest->link); + p = forest->link; + while (p->link != forest) + p = p->link; + p->link = forest->link; + forest = p; +} +Tree cvtconst(Tree p) { + Symbol q = constant(p->type, p->u.v); + Tree e; + + if (q->u.c.loc == NULL) + q->u.c.loc = genident(STATIC, p->type, GLOBAL); + if (isarray(p->type)) { + e = simplify(ADDRG, atop(p->type), NULL, NULL); + e->u.sym = q->u.c.loc; + } else + e = idtree(q->u.c.loc); + return e; +} +void gencode(Symbol caller[], Symbol callee[]) { + Code cp; + Coordinate save; + + if (prunetemps == -1) + prunetemps = !IR->wants_dag; + save = src; + if (assignargs) { + int i; + Symbol p, q; + cp = codehead.next->next; + codelist = codehead.next; + for (i = 0; (p = callee[i]) != NULL + && (q = caller[i]) != NULL; i++) + if (p->sclass != q->sclass || p->type != q->type) + walk(asgn(p, idtree(q)), 0, 0); + codelist->next = cp; + cp->prev = codelist; + } + if (glevel && IR->stabsym) { + int i; + Symbol p, q; + for (i = 0; (p = callee[i]) != NULL + && (q = caller[i]) != NULL; i++) { + (*IR->stabsym)(p); + if (p->sclass != q->sclass || p->type != q->type) + (*IR->stabsym)(q); + } + swtoseg(CODE); + } + cp = codehead.next; + for ( ; errcnt <= 0 && cp; cp = cp->next) + switch (cp->kind) { + case Address: (*IR->address)(cp->u.addr.sym, cp->u.addr.base, + cp->u.addr.offset); break; + case Blockbeg: { + Symbol *p = cp->u.block.locals; + (*IR->blockbeg)(&cp->u.block.x); + for ( ; *p; p++) + if ((*p)->ref != 0.0) + (*IR->local)(*p); + else if (glevel) (*IR->local)(*p); + } + break; + case Blockend: (*IR->blockend)(&cp->u.begin->u.block.x); break; + case Defpoint: src = cp->u.point.src; break; + case Gen: case Jump: + case Label: if (prunetemps) + cp->u.forest = prune(cp->u.forest); + fixup(cp->u.forest); + cp->u.forest = (*IR->gen)(cp->u.forest); break; + case Local: (*IR->local)(cp->u.var); break; + case Switch: break; + default: assert(0); + } + src = save; +} +static void fixup(Node p) { + for ( ; p; p = p->link) + switch (generic(p->op)) { + case JUMP: + if (specific(p->kids[0]->op) == ADDRG+P) + p->kids[0]->syms[0] = + equated(p->kids[0]->syms[0]); + break; + case LABEL: assert(p->syms[0] == equated(p->syms[0])); break; + case EQ: case GE: case GT: case LE: case LT: case NE: + assert(p->syms[0]); + p->syms[0] = equated(p->syms[0]); + } +} +static Symbol equated(Symbol p) { + { Symbol q; for (q = p->u.l.equatedto; q; q = q->u.l.equatedto) assert(p != q); } + while (p->u.l.equatedto) + p = p->u.l.equatedto; + return p; +} +void emitcode(void) { + Code cp; + Coordinate save; + + save = src; + cp = codehead.next; + for ( ; errcnt <= 0 && cp; cp = cp->next) + switch (cp->kind) { + case Address: break; + case Blockbeg: if (glevel && IR->stabblock) { + (*IR->stabblock)('{', cp->u.block.level - LOCAL, cp->u.block.locals); + swtoseg(CODE); + } + break; + case Blockend: if (glevel && IR->stabblock) { + Code bp = cp->u.begin; + foreach(bp->u.block.identifiers, bp->u.block.level, typestab, NULL); + foreach(bp->u.block.types, bp->u.block.level, typestab, NULL); + (*IR->stabblock)('}', bp->u.block.level - LOCAL, bp->u.block.locals); + swtoseg(CODE); + } + break; + case Defpoint: src = cp->u.point.src; + if (glevel > 0 && IR->stabline) { + (*IR->stabline)(&cp->u.point.src); swtoseg(CODE); } break; + case Gen: case Jump: + case Label: if (cp->u.forest) + (*IR->emit)(cp->u.forest); break; + case Local: if (glevel && IR->stabsym) { + (*IR->stabsym)(cp->u.var); + swtoseg(CODE); + } break; + case Switch: { int i; + defglobal(cp->u.swtch.table, LIT); + (*IR->defaddress)(equated(cp->u.swtch.labels[0])); + for (i = 1; i < cp->u.swtch.size; i++) { + long k = cp->u.swtch.values[i-1]; + while (++k < cp->u.swtch.values[i]) + assert(k < LONG_MAX), + (*IR->defaddress)(equated(cp->u.swtch.deflab)); + (*IR->defaddress)(equated(cp->u.swtch.labels[i])); + } + swtoseg(CODE); + } break; + default: assert(0); + } + src = save; +} + +static Node undag(Node forest) { + Node p; + + tail = &forest; + for (p = forest; p; p = p->link) + if (generic(p->op) == INDIR) { + assert(p->count >= 1); + visit(p, 1); + if (p->syms[2]) { + assert(p->syms[2]->u.t.cse); + p->syms[2]->u.t.cse = NULL; + addlocal(p->syms[2]); + } + } else if (iscall(p->op) && p->count >= 1) + visit(p, 1); + else { + assert(p->count == 0), + visit(p, 1); + *tail = p; + tail = &p->link; + } + *tail = NULL; + return forest; +} +static Node replace(Node p) { + if (p && ( generic(p->op) == INDIR + && generic(p->kids[0]->op) == ADDRL + && p->kids[0]->syms[0]->temporary + && p->kids[0]->syms[0]->u.t.replace)) { + p = p->kids[0]->syms[0]->u.t.cse; + if (generic(p->op) == INDIR && isaddrop(p->kids[0]->op)) + p = newnode(p->op, newnode(p->kids[0]->op, NULL, NULL, + p->kids[0]->syms[0]), NULL, NULL); + else if (generic(p->op) == ADDRG) + p = newnode(p->op, NULL, NULL, p->syms[0]); + else + assert(0); + p->count = 1; + } else if (p) { + p->kids[0] = replace(p->kids[0]); + p->kids[1] = replace(p->kids[1]); + } + return p; +} +static Node prune(Node forest) { + Node p, *tail = &forest; + int count = 0; + + for (p = forest; p; p = p->link) { + if (count > 0) { + p->kids[0] = replace(p->kids[0]); + p->kids[1] = replace(p->kids[1]); + } + if (( generic(p->op) == ASGN + && generic(p->kids[0]->op) == ADDRL + && p->kids[0]->syms[0]->temporary + && p->kids[0]->syms[0]->u.t.cse == p->kids[1])) { + Symbol tmp = p->kids[0]->syms[0]; + if (!tmp->defined) + (*IR->local)(tmp); + tmp->defined = 1; + if (( generic(p->kids[1]->op) == INDIR + && isaddrop(p->kids[1]->kids[0]->op) + && p->kids[1]->kids[0]->syms[0]->sclass == REGISTER) + || (( generic(p->kids[1]->op) == INDIR + && isaddrop(p->kids[1]->kids[0]->op)) && tmp->sclass == AUTO) + || (generic(p->kids[1]->op) == ADDRG && tmp->sclass == AUTO)) { + tmp->u.t.replace = 1; + count++; + continue; /* and omit the assignment */ + } + } + /* keep the assignment and other roots */ + *tail = p; + tail = &(*tail)->link; + } + assert(*tail == NULL); + return forest; +} +static Node visit(Node p, int listed) { + if (p) { + if (p->syms[2]) + p = tmpnode(p); + else if ((p->count <= 1 && !iscall(p->op)) + || (p->count == 0 && iscall(p->op))) { + p->kids[0] = visit(p->kids[0], 0); + p->kids[1] = visit(p->kids[1], 0); + } + + else if (specific(p->op) == ADDRL+P || specific(p->op) == ADDRF+P) { + assert(!listed); + p = newnode(p->op, NULL, NULL, p->syms[0]); + p->count = 1; + } + else if (p->op == INDIR+B) { + p = newnode(p->op, p->kids[0], NULL, NULL); + p->count = 1; + p->kids[0] = visit(p->kids[0], 0); + p->kids[1] = visit(p->kids[1], 0); + } + else { + p->kids[0] = visit(p->kids[0], 0); + p->kids[1] = visit(p->kids[1], 0); + p->syms[2] = temporary(REGISTER, btot(p->op, opsize(p->op))); + assert(!p->syms[2]->defined); + p->syms[2]->ref = 1; + p->syms[2]->u.t.cse = p; + + *tail = asgnnode(p->syms[2], p); + tail = &(*tail)->link; + if (!listed) + p = tmpnode(p); + }; + } + return p; +} +static Node tmpnode(Node p) { + Symbol tmp = p->syms[2]; + + assert(tmp); + if (--p->count == 0) + p->syms[2] = NULL; + p = newnode(INDIR + ttob(tmp->type), + newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), NULL, NULL); + p->count = 1; + return p; +} +static Node asgnnode(Symbol tmp, Node p) { + p = newnode(ASGN + ttob(tmp->type), + newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), p, NULL); + p->syms[0] = intconst(tmp->type->size); + p->syms[1] = intconst(tmp->type->align); + return p; +} +/* printdag - print dag p on fd, or the node list if p == 0 */ +void printdag(Node p, int fd) { + FILE *f = fd == 1 ? stdout : stderr; + + printed(0); + if (p == 0) { + if ((p = forest) != NULL) + do { + p = p->link; + printdag1(p, fd, 0); + } while (p != forest); + } else if (*printed(nodeid((Tree)p))) + fprint(f, "node'%d printed above\n", nodeid((Tree)p)); + else + printdag1(p, fd, 0); +} + +/* printdag1 - recursively print dag p */ +static void printdag1(Node p, int fd, int lev) { + int id, i; + + if (p == 0 || *printed(id = nodeid((Tree)p))) + return; + *printed(id) = 1; + for (i = 0; i < NELEMS(p->kids); i++) + printdag1(p->kids[i], fd, lev + 1); + printnode(p, fd, lev); +} + +/* printnode - print fields of dag p */ +static void printnode(Node p, int fd, int lev) { + if (p) { + FILE *f = fd == 1 ? stdout : stderr; + int i, id = nodeid((Tree)p); + fprint(f, "%c%d%s", lev == 0 ? '\'' : '#', id, + &" "[id < 10 ? 0 : id < 100 ? 1 : 2]); + fprint(f, "%s count=%d", opname(p->op), p->count); + for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++) + fprint(f, " #%d", nodeid((Tree)p->kids[i])); + if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type) + fprint(f, " {%t}", p->syms[0]->type); + else + for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++) + if (p->syms[i]->name) + fprint(f, " %s", p->syms[i]->name); + else + fprint(f, " %p", p->syms[i]); + fprint(f, "\n"); + } +} + +/* typestab - emit stab entries for p */ +static void typestab(Symbol p, void *cl) { + if (!isfunc(p->type) && (p->sclass == EXTERN || p->sclass == STATIC) && IR->stabsym) + (*IR->stabsym)(p); + else if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype) + (*IR->stabtype)(p); +} + diff --git a/src/tools/lcc/src/dagcheck.md b/src/tools/lcc/src/dagcheck.md new file mode 100644 index 0000000..292dbee --- /dev/null +++ b/src/tools/lcc/src/dagcheck.md @@ -0,0 +1,210 @@ +%{ +#include "c.h" +typedef Node NODEPTR_TYPE; +#define OP_LABEL(p) (specific((p)->op)) +#define LEFT_CHILD(p) ((p)->kids[0]) +#define RIGHT_CHILD(p) ((p)->kids[1]) +#define STATE_LABEL(p) ((p)->x.state) +#define PANIC error +%} +%term CNSTF=17 CNSTI=21 CNSTP=23 CNSTU=22 +%term ARGB=41 ARGF=33 ARGI=37 ARGP=39 ARGU=38 +%term ASGNB=57 ASGNF=49 ASGNI=53 ASGNP=55 ASGNU=54 +%term INDIRB=73 INDIRF=65 INDIRI=69 INDIRP=71 INDIRU=70 +%term CVFF=113 CVFI=117 +%term CVIF=129 CVII=133 CVIU=134 +%term CVPP=151 CVPU=150 +%term CVUI=181 CVUP=183 CVUU=182 +%term NEGF=193 NEGI=197 +%term CALLB=217 CALLF=209 CALLI=213 CALLP=215 CALLU=214 CALLV=216 +%term RETF=241 RETI=245 RETP=247 RETU=246 RETV=248 +%term ADDRGP=263 +%term ADDRFP=279 +%term ADDRLP=295 +%term ADDF=305 ADDI=309 ADDP=311 ADDU=310 +%term SUBF=321 SUBI=325 SUBP=327 SUBU=326 +%term LSHI=341 LSHU=342 +%term MODI=357 MODU=358 +%term RSHI=373 RSHU=374 +%term BANDI=389 BANDU=390 +%term BCOMI=405 BCOMU=406 +%term BORI=421 BORU=422 +%term BXORI=437 BXORU=438 +%term DIVF=449 DIVI=453 DIVU=454 +%term MULF=465 MULI=469 MULU=470 +%term EQF=481 EQI=485 EQU=486 +%term GEF=497 GEI=501 GEU=502 +%term GTF=513 GTI=517 GTU=518 +%term LEF=529 LEI=533 LEU=534 +%term LTF=545 LTI=549 LTU=550 +%term NEF=561 NEI=565 NEU=566 +%term JUMPV=584 +%term LABELV=600 +%% +stmt: INDIRB(P) "" +stmt: INDIRF(P) "" +stmt: INDIRI(P) "" +stmt: INDIRU(P) "" +stmt: INDIRP(P) "" +stmt: CALLF(P) "" +stmt: CALLI(P) "" +stmt: CALLU(P) "" +stmt: CALLP(P) "" +stmt: V "" +bogus: I "" 1 +bogus: U "" 1 +bogus: P "" 1 +bogus: F "" 1 +bogus: B "" 1 +bogus: V "" 1 +I: bogus "" 1 +U: bogus "" 1 +P: bogus "" 1 +F: bogus "" 1 +B: bogus "" 1 +V: bogus "" 1 +F: CNSTF "" +I: CNSTI "" +P: CNSTP "" +U: CNSTU "" +V: ARGB(B) "" +V: ARGF(F) "" +V: ARGI(I) "" +V: ARGU(U) "" +V: ARGP(P) "" +V: ASGNB(P,B) "" +V: ASGNF(P,F) "" +V: ASGNI(P,I) "" +V: ASGNU(P,U) "" +V: ASGNP(P,P) "" +B: INDIRB(P) "" +F: INDIRF(P) "" +I: INDIRI(P) "" +U: INDIRU(P) "" +P: INDIRP(P) "" +I: CVII(I) "" +I: CVUI(U) "" +I: CVFI(F) "" +U: CVIU(I) "" +U: CVUU(U) "" +U: CVPU(P) "" +F: CVIF(I) "" +F: CVFF(F) "" +P: CVUP(U) "" +P: CVPP(P) "" +F: NEGF(F) "" +I: NEGI(I) "" +V: CALLB(P,P) "" +F: CALLF(P) "" +I: CALLI(P) "" +U: CALLU(P) "" +P: CALLP(P) "" +V: CALLV(P) "" +V: RETF(F) "" +V: RETI(I) "" +V: RETU(U) "" +V: RETP(P) "" +V: RETV "" +P: ADDRGP "" +P: ADDRFP "" +P: ADDRLP "" +F: ADDF(F,F) "" +I: ADDI(I,I) "" +P: ADDP(P,I) "" +P: ADDP(I,P) "" +P: ADDP(U,P) "" +P: ADDP(P,U) "" +U: ADDU(U,U) "" +F: SUBF(F,F) "" +I: SUBI(I,I) "" +P: SUBP(P,I) "" +P: SUBP(P,U) "" +U: SUBU(U,U) "" +I: LSHI(I,I) "" +U: LSHU(U,I) "" +I: MODI(I,I) "" +U: MODU(U,U) "" +I: RSHI(I,I) "" +U: RSHU(U,I) "" +U: BANDU(U,U) "" +I: BANDI(I,I) "" +U: BCOMU(U) "" +I: BCOMI(I) "" +I: BORI(I,I) "" +U: BORU(U,U) "" +U: BXORU(U,U) "" +I: BXORI(I,I) "" +F: DIVF(F,F) "" +I: DIVI(I,I) "" +U: DIVU(U,U) "" +F: MULF(F,F) "" +I: MULI(I,I) "" +U: MULU(U,U) "" +V: EQF(F,F) "" +V: EQI(I,I) "" +V: EQU(U,U) "" +V: GEF(F,F) "" +V: GEI(I,I) "" +V: GEU(U,U) "" +V: GTF(F,F) "" +V: GTI(I,I) "" +V: GTU(U,U) "" +V: LEF(F,F) "" +V: LEI(I,I) "" +V: LEU(U,U) "" +V: LTF(F,F) "" +V: LTI(I,I) "" +V: LTU(U,U) "" +V: NEF(F,F) "" +V: NEI(I,I) "" +V: NEU(U,U) "" +V: JUMPV(P) "" +V: LABELV "" +%% + +static void reduce(NODEPTR_TYPE p, int goalnt) { + int i, sz = opsize(p->op), rulenumber = _rule(p->x.state, goalnt); + short *nts = _nts[rulenumber]; + NODEPTR_TYPE kids[10]; + + assert(rulenumber); + _kids(p, rulenumber, kids); + for (i = 0; nts[i]; i++) + reduce(kids[i], nts[i]); + switch (optype(p->op)) { +#define xx(ty) if (sz == ty->size) return + case I: + case U: + xx(chartype); + xx(shorttype); + xx(inttype); + xx(longtype); + xx(longlong); + break; + case F: + xx(floattype); + xx(doubletype); + xx(longdouble); + break; + case P: + xx(voidptype); + xx(funcptype); + break; + case V: + case B: if (sz == 0) return; +#undef xx + } + printdag(p, 2); + assert(0); +} + +void check(Node p) { + struct _state { short cost[1]; }; + + _label(p); + if (((struct _state *)p->x.state)->cost[1] > 0) { + printdag(p, 2); + assert(0); + } + reduce(p, 1); +} diff --git a/src/tools/lcc/src/decl.c b/src/tools/lcc/src/decl.c new file mode 100644 index 0000000..132241e --- /dev/null +++ b/src/tools/lcc/src/decl.c @@ -0,0 +1,1162 @@ +#include "c.h" + + +#define add(x,n) (x > inttype->u.sym->u.limits.max.i-(n) ? (overflow=1,x) : x+(n)) +#define chkoverflow(x,n) ((void)add(x,n)) +#define bits2bytes(n) (((n) + 7)/8) +static int regcount; + +static List autos, registers; +Symbol cfunc; /* current function */ +Symbol retv; /* return value location for structs */ + +static void checkref(Symbol, void *); +static Symbol dclglobal(int, char *, Type, Coordinate *); +static Symbol dcllocal(int, char *, Type, Coordinate *); +static Symbol dclparam(int, char *, Type, Coordinate *); +static Type dclr(Type, char **, Symbol **, int); +static Type dclr1(char **, Symbol **, int); +static void decl(Symbol (*)(int, char *, Type, Coordinate *)); +extern void doconst(Symbol, void *); +static void doglobal(Symbol, void *); +static void doextern(Symbol, void *); +static void exitparams(Symbol []); +static void fields(Type); +static void funcdefn(int, char *, Type, Symbol [], Coordinate); +static void initglobal(Symbol, int); +static void oldparam(Symbol, void *); +static Symbol *parameters(Type); +static Type specifier(int *); +static Type structdcl(int); +static Type tnode(int, Type); +void program(void) { + int n; + + level = GLOBAL; + for (n = 0; t != EOI; n++) + if (kind[t] == CHAR || kind[t] == STATIC + || t == ID || t == '*' || t == '(') { + decl(dclglobal); + deallocate(STMT); + if (!(glevel >= 3 || xref)) + deallocate(FUNC); + } else if (t == ';') { + warning("empty declaration\n"); + t = gettok(); + } else { + error("unrecognized declaration\n"); + t = gettok(); + } + if (n == 0) + warning("empty input file\n"); +} +static Type specifier(int *sclass) { + int cls, cons, sign, size, type, vol; + Type ty = NULL; + + cls = vol = cons = sign = size = type = 0; + if (sclass == NULL) + cls = AUTO; + for (;;) { + int *p, tt = t; + switch (t) { + case AUTO: + case REGISTER: if (level <= GLOBAL && cls == 0) + error("invalid use of `%k'\n", t); + p = &cls; t = gettok(); break; + case STATIC: case EXTERN: + case TYPEDEF: p = &cls; t = gettok(); break; + case CONST: p = &cons; t = gettok(); break; + case VOLATILE: p = &vol; t = gettok(); break; + case SIGNED: + case UNSIGNED: p = &sign; t = gettok(); break; + case LONG: if (size == LONG) { + size = 0; + tt = LONG+LONG; + } + p = &size; t = gettok(); break; + case SHORT: p = &size; t = gettok(); break; + case VOID: case CHAR: case INT: case FLOAT: + case DOUBLE: p = &type; ty = tsym->type; + t = gettok(); break; + case ENUM: p = &type; ty = enumdcl(); break; + case STRUCT: + case UNION: p = &type; ty = structdcl(t); break; + case ID: + if (istypename(t, tsym) && type == 0 + && sign == 0 && size == 0) { + use(tsym, src); + ty = tsym->type; + if (isqual(ty) + && ty->size != ty->type->size) { + ty = unqual(ty); + if (isconst(tsym->type)) + ty = qual(CONST, ty); + if (isvolatile(tsym->type)) + ty = qual(VOLATILE, ty); + tsym->type = ty; + } + p = &type; + t = gettok(); + } else + p = NULL; + break; + default: p = NULL; + } + if (p == NULL) + break; + if (*p) + error("invalid use of `%k'\n", tt); + *p = tt; + } + if (sclass) + *sclass = cls; + if (type == 0) { + type = INT; + ty = inttype; + } + if ((size == SHORT && type != INT) + || (size == LONG+LONG && type != INT) + || (size == LONG && type != INT && type != DOUBLE) + || (sign && type != INT && type != CHAR)) + error("invalid type specification\n"); + if (type == CHAR && sign) + ty = sign == UNSIGNED ? unsignedchar : signedchar; + else if (size == SHORT) + ty = sign == UNSIGNED ? unsignedshort : shorttype; + else if (size == LONG && type == DOUBLE) + ty = longdouble; + else if (size == LONG+LONG) { + ty = sign == UNSIGNED ? unsignedlonglong : longlong; + if (Aflag >= 1) + warning("`%t' is a non-ANSI type\n", ty); + } else if (size == LONG) + ty = sign == UNSIGNED ? unsignedlong : longtype; + else if (sign == UNSIGNED && type == INT) + ty = unsignedtype; + if (cons == CONST) + ty = qual(CONST, ty); + if (vol == VOLATILE) + ty = qual(VOLATILE, ty); + return ty; +} +static void decl(Symbol (*dcl)(int, char *, Type, Coordinate *)) { + int sclass; + Type ty, ty1; + static char stop[] = { CHAR, STATIC, ID, 0 }; + + ty = specifier(&sclass); + if (t == ID || t == '*' || t == '(' || t == '[') { + char *id; + Coordinate pos; + id = NULL; + pos = src; + if (level == GLOBAL) { + Symbol *params = NULL; + ty1 = dclr(ty, &id, ¶ms, 0); + if (params && id && isfunc(ty1) + && (t == '{' || istypename(t, tsym) + || (kind[t] == STATIC && t != TYPEDEF))) { + if (sclass == TYPEDEF) { + error("invalid use of `typedef'\n"); + sclass = EXTERN; + } + if (ty1->u.f.oldstyle) + exitscope(); + funcdefn(sclass, id, ty1, params, pos); + return; + } else if (params) + exitparams(params); + } else + ty1 = dclr(ty, &id, NULL, 0); + for (;;) { + if (Aflag >= 1 && !hasproto(ty1)) + warning("missing prototype\n"); + if (id == NULL) + error("missing identifier\n"); + else if (sclass == TYPEDEF) + { + Symbol p = lookup(id, identifiers); + if (p && p->scope == level) + error("redeclaration of `%s'\n", id); + p = install(id, &identifiers, level, + level < LOCAL ? PERM : FUNC); + p->type = ty1; + p->sclass = TYPEDEF; + p->src = pos; + } + else + (void)(*dcl)(sclass, id, ty1, &pos); + if (t != ',') + break; + t = gettok(); + id = NULL; + pos = src; + ty1 = dclr(ty, &id, NULL, 0); + } + } else if (ty == NULL + || !(isenum(ty) || + (isstruct(ty) && (*unqual(ty)->u.sym->name < '1' || *unqual(ty)->u.sym->name > '9')))) + error("empty declaration\n"); + test(';', stop); +} +static Symbol dclglobal(int sclass, char *id, Type ty, Coordinate *pos) { + Symbol p; + + if (sclass == 0) + sclass = AUTO; + else if (sclass != EXTERN && sclass != STATIC) { + error("invalid storage class `%k' for `%t %s'\n", + sclass, ty, id); + sclass = AUTO; + } + p = lookup(id, identifiers); + if (p && p->scope == GLOBAL) { + if (p->sclass != TYPEDEF && eqtype(ty, p->type, 1)) + ty = compose(ty, p->type); + else + error("redeclaration of `%s' previously declared at %w\n", p->name, &p->src); + + if (!isfunc(ty) && p->defined && t == '=') + error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); + + if ((p->sclass == EXTERN && sclass == STATIC) + || (p->sclass == STATIC && sclass == AUTO) + || (p->sclass == AUTO && sclass == STATIC)) + warning("inconsistent linkage for `%s' previously declared at %w\n", p->name, &p->src); + + } + if (p == NULL || p->scope != GLOBAL) { + Symbol q = lookup(id, externals); + if (q) { + if (sclass == STATIC || !eqtype(ty, q->type, 1)) + warning("declaration of `%s' does not match previous declaration at %w\n", id, &q->src); + + p = relocate(id, externals, globals); + p->sclass = sclass; + } else { + p = install(id, &globals, GLOBAL, PERM); + p->sclass = sclass; + (*IR->defsymbol)(p); + } + if (p->sclass != STATIC) { + static int nglobals; + nglobals++; + if (Aflag >= 2 && nglobals == 512) + warning("more than 511 external identifiers\n"); + } + } else if (p->sclass == EXTERN) + p->sclass = sclass; + p->type = ty; + p->src = *pos; + if (t == '=' && isfunc(p->type)) { + error("illegal initialization for `%s'\n", p->name); + t = gettok(); + initializer(p->type, 0); + } else if (t == '=') { + initglobal(p, 0); + if (glevel > 0 && IR->stabsym) { + (*IR->stabsym)(p); swtoseg(p->u.seg); } + } else if (p->sclass == STATIC && !isfunc(p->type) + && p->type->size == 0) + error("undefined size for `%t %s'\n", p->type, p->name); + return p; +} +static void initglobal(Symbol p, int flag) { + Type ty; + + if (t == '=' || flag) { + if (p->sclass == STATIC) { + for (ty = p->type; isarray(ty); ty = ty->type) + ; + defglobal(p, isconst(ty) ? LIT : DATA); + } else + defglobal(p, DATA); + if (t == '=') + t = gettok(); + ty = initializer(p->type, 0); + if (isarray(p->type) && p->type->size == 0) + p->type = ty; + if (p->sclass == EXTERN) + p->sclass = AUTO; + } +} +void defglobal(Symbol p, int seg) { + p->u.seg = seg; + swtoseg(p->u.seg); + if (p->sclass != STATIC) + (*IR->export)(p); + (*IR->global)(p); + p->defined = 1; +} + +static Type dclr(Type basety, char **id, Symbol **params, int abstract) { + Type ty = dclr1(id, params, abstract); + + for ( ; ty; ty = ty->type) + switch (ty->op) { + case POINTER: + basety = ptr(basety); + break; + case FUNCTION: + basety = func(basety, ty->u.f.proto, + ty->u.f.oldstyle); + break; + case ARRAY: + basety = array(basety, ty->size, 0); + break; + case CONST: case VOLATILE: + basety = qual(ty->op, basety); + break; + default: assert(0); + } + if (Aflag >= 2 && basety->size > 32767) + warning("more than 32767 bytes in `%t'\n", basety); + return basety; +} +static Type tnode(int op, Type type) { + Type ty; + + NEW0(ty, STMT); + ty->op = op; + ty->type = type; + return ty; +} +static Type dclr1(char **id, Symbol **params, int abstract) { + Type ty = NULL; + + switch (t) { + case ID: if (id) + *id = token; + else + error("extraneous identifier `%s'\n", token); + t = gettok(); break; + case '*': t = gettok(); if (t == CONST || t == VOLATILE) { + Type ty1; + ty1 = ty = tnode(t, NULL); + while ((t = gettok()) == CONST || t == VOLATILE) + ty1 = tnode(t, ty1); + ty->type = dclr1(id, params, abstract); + ty = ty1; + } else + ty = dclr1(id, params, abstract); + ty = tnode(POINTER, ty); break; + case '(': t = gettok(); if (abstract + && (t == REGISTER || istypename(t, tsym) || t == ')')) { + Symbol *args; + ty = tnode(FUNCTION, ty); + enterscope(); + if (level > PARAM) + enterscope(); + args = parameters(ty); + exitparams(args); + } else { + ty = dclr1(id, params, abstract); + expect(')'); + if (abstract && ty == NULL + && (id == NULL || *id == NULL)) + return tnode(FUNCTION, NULL); + } break; + case '[': break; + default: return ty; + } + while (t == '(' || t == '[') + switch (t) { + case '(': t = gettok(); { Symbol *args; + ty = tnode(FUNCTION, ty); + enterscope(); + if (level > PARAM) + enterscope(); + args = parameters(ty); + if (params && *params == NULL) + *params = args; + else + exitparams(args); + } + break; + case '[': t = gettok(); { int n = 0; + if (kind[t] == ID) { + n = intexpr(']', 1); + if (n <= 0) { + error("`%d' is an illegal array size\n", n); + n = 1; + } + } else + expect(']'); + ty = tnode(ARRAY, ty); + ty->size = n; } break; + default: assert(0); + } + return ty; +} +static Symbol *parameters(Type fty) { + List list = NULL; + Symbol *params; + + if (kind[t] == STATIC || istypename(t, tsym)) { + int n = 0; + Type ty1 = NULL; + for (;;) { + Type ty; + int sclass = 0; + char *id = NULL; + if (ty1 && t == ELLIPSIS) { + static struct symbol sentinel; + if (sentinel.type == NULL) { + sentinel.type = voidtype; + sentinel.defined = 1; + } + if (ty1 == voidtype) + error("illegal formal parameter types\n"); + list = append(&sentinel, list); + t = gettok(); + break; + } + if (!istypename(t, tsym) && t != REGISTER) + error("missing parameter type\n"); + n++; + ty = dclr(specifier(&sclass), &id, NULL, 1); + if ( (ty == voidtype && (ty1 || id)) + || ty1 == voidtype) + error("illegal formal parameter types\n"); + if (id == NULL) + id = stringd(n); + if (ty != voidtype) + list = append(dclparam(sclass, id, ty, &src), list); + if (Aflag >= 1 && !hasproto(ty)) + warning("missing prototype\n"); + if (ty1 == NULL) + ty1 = ty; + if (t != ',') + break; + t = gettok(); + } + fty->u.f.proto = newarray(length(list) + 1, + sizeof (Type *), PERM); + params = ltov(&list, FUNC); + for (n = 0; params[n]; n++) + fty->u.f.proto[n] = params[n]->type; + fty->u.f.proto[n] = NULL; + fty->u.f.oldstyle = 0; + } else { + if (t == ID) + for (;;) { + Symbol p; + if (t != ID) { + error("expecting an identifier\n"); + break; + } + p = dclparam(0, token, inttype, &src); + p->defined = 0; + list = append(p, list); + t = gettok(); + if (t != ',') + break; + t = gettok(); + } + params = ltov(&list, FUNC); + fty->u.f.proto = NULL; + fty->u.f.oldstyle = 1; + } + if (t != ')') { + static char stop[] = { CHAR, STATIC, IF, ')', 0 }; + expect(')'); + skipto('{', stop); + } + if (t == ')') + t = gettok(); + return params; +} +static void exitparams(Symbol params[]) { + assert(params); + if (params[0] && !params[0]->defined) + error("extraneous old-style parameter list\n"); + if (level > PARAM) + exitscope(); + exitscope(); +} + +static Symbol dclparam(int sclass, char *id, Type ty, Coordinate *pos) { + Symbol p; + + if (isfunc(ty)) + ty = ptr(ty); + else if (isarray(ty)) + ty = atop(ty); + if (sclass == 0) + sclass = AUTO; + else if (sclass != REGISTER) { + error("invalid storage class `%k' for `%t%s\n", + sclass, ty, stringf(id ? " %s'" : "' parameter", id)); + sclass = AUTO; + } else if (isvolatile(ty) || isstruct(ty)) { + warning("register declaration ignored for `%t%s\n", + ty, stringf(id ? " %s'" : "' parameter", id)); + sclass = AUTO; + } + + p = lookup(id, identifiers); + if (p && p->scope == level) + error("duplicate declaration for `%s' previously declared at %w\n", id, &p->src); + + else + p = install(id, &identifiers, level, FUNC); + p->sclass = sclass; + p->src = *pos; + p->type = ty; + p->defined = 1; + if (t == '=') { + error("illegal initialization for parameter `%s'\n", id); + t = gettok(); + (void)expr1(0); + } + return p; +} +static Type structdcl(int op) { + char *tag; + Type ty; + Symbol p; + Coordinate pos; + + t = gettok(); + pos = src; + if (t == ID) { + tag = token; + t = gettok(); + } else + tag = ""; + if (t == '{') { + static char stop[] = { IF, ',', 0 }; + ty = newstruct(op, tag); + ty->u.sym->src = pos; + ty->u.sym->defined = 1; + t = gettok(); + if (istypename(t, tsym)) + fields(ty); + else + error("invalid %k field declarations\n", op); + test('}', stop); + } + else if (*tag && (p = lookup(tag, types)) != NULL + && p->type->op == op) { + ty = p->type; + if (t == ';' && p->scope < level) + ty = newstruct(op, tag); + } + else { + if (*tag == 0) + error("missing %k tag\n", op); + ty = newstruct(op, tag); + } + if (*tag && xref) + use(ty->u.sym, pos); + return ty; +} +static void fields(Type ty) { + { int n = 0; + while (istypename(t, tsym)) { + static char stop[] = { IF, CHAR, '}', 0 }; + Type ty1 = specifier(NULL); + for (;;) { + Field p; + char *id = NULL; + Type fty = dclr(ty1, &id, NULL, 0); + p = newfield(id, ty, fty); + if (Aflag >= 1 && !hasproto(p->type)) + warning("missing prototype\n"); + if (t == ':') { + if (unqual(p->type) != inttype + && unqual(p->type) != unsignedtype) { + error("`%t' is an illegal bit-field type\n", + p->type); + p->type = inttype; + } + t = gettok(); + p->bitsize = intexpr(0, 0); + if (p->bitsize > 8*inttype->size || p->bitsize < 0) { + error("`%d' is an illegal bit-field size\n", + p->bitsize); + p->bitsize = 8*inttype->size; + } else if (p->bitsize == 0 && id) { + warning("extraneous 0-width bit field `%t %s' ignored\n", p->type, id); + + p->name = stringd(genlabel(1)); + } + p->lsb = 1; + } + else { + if (id == NULL) + error("field name missing\n"); + else if (isfunc(p->type)) + error("`%t' is an illegal field type\n", p->type); + else if (p->type->size == 0) + error("undefined size for field `%t %s'\n", + p->type, id); + } + if (isconst(p->type)) + ty->u.sym->u.s.cfields = 1; + if (isvolatile(p->type)) + ty->u.sym->u.s.vfields = 1; + n++; + if (Aflag >= 2 && n == 128) + warning("more than 127 fields in `%t'\n", ty); + if (t != ',') + break; + t = gettok(); + } + test(';', stop); + } } + { int bits = 0, off = 0, overflow = 0; + Field p, *q = &ty->u.sym->u.s.flist; + ty->align = IR->structmetric.align; + for (p = *q; p; p = p->link) { + int a = p->type->align ? p->type->align : 1; + if (p->lsb) + a = unsignedtype->align; + if (ty->op == UNION) + off = bits = 0; + else if (p->bitsize == 0 || bits == 0 + || bits - 1 + p->bitsize > 8*unsignedtype->size) { + off = add(off, bits2bytes(bits-1)); + bits = 0; + chkoverflow(off, a - 1); + off = roundup(off, a); + } + if (a > ty->align) + ty->align = a; + p->offset = off; + + if (p->lsb) { + if (bits == 0) + bits = 1; + if (IR->little_endian) + p->lsb = bits; + else + p->lsb = 8*unsignedtype->size - bits + 1 + - p->bitsize + 1; + bits += p->bitsize; + } else + off = add(off, p->type->size); + if (off + bits2bytes(bits-1) > ty->size) + ty->size = off + bits2bytes(bits-1); + if (p->name == NULL + || !('1' <= *p->name && *p->name <= '9')) { + *q = p; + q = &p->link; + } + } + *q = NULL; + chkoverflow(ty->size, ty->align - 1); + ty->size = roundup(ty->size, ty->align); + if (overflow) { + error("size of `%t' exceeds %d bytes\n", ty, inttype->u.sym->u.limits.max.i); + ty->size = inttype->u.sym->u.limits.max.i&(~(ty->align - 1)); + } } +} +static void funcdefn(int sclass, char *id, Type ty, Symbol params[], Coordinate pt) { + int i, n; + Symbol *callee, *caller, p; + Type rty = freturn(ty); + + if (isstruct(rty) && rty->size == 0) + error("illegal use of incomplete type `%t'\n", rty); + for (n = 0; params[n]; n++) + ; + if (n > 0 && params[n-1]->name == NULL) + params[--n] = NULL; + if (Aflag >= 2 && n > 31) + warning("more than 31 parameters in function `%s'\n", id); + if (ty->u.f.oldstyle) { + if (Aflag >= 1) + warning("old-style function definition for `%s'\n", id); + caller = params; + callee = newarray(n + 1, sizeof *callee, FUNC); + memcpy(callee, caller, (n+1)*sizeof *callee); + enterscope(); + assert(level == PARAM); + while (kind[t] == STATIC || istypename(t, tsym)) + decl(dclparam); + foreach(identifiers, PARAM, oldparam, callee); + + for (i = 0; (p = callee[i]) != NULL; i++) { + if (!p->defined) + callee[i] = dclparam(0, p->name, inttype, &p->src); + *caller[i] = *p; + caller[i]->sclass = AUTO; + caller[i]->type = promote(p->type); + } + p = lookup(id, identifiers); + if (p && p->scope == GLOBAL && isfunc(p->type) + && p->type->u.f.proto) { + Type *proto = p->type->u.f.proto; + for (i = 0; caller[i] && proto[i]; i++) { + Type ty = unqual(proto[i]); + if (eqtype(isenum(ty) ? ty->type : ty, + unqual(caller[i]->type), 1) == 0) + break; + else if (isenum(ty) && !isenum(unqual(caller[i]->type))) + warning("compatibility of `%t' and `%t' is compiler dependent\n", + proto[i], caller[i]->type); + } + if (proto[i] || caller[i]) + error("conflicting argument declarations for function `%s'\n", id); + + } + else { + Type *proto = newarray(n + 1, sizeof *proto, PERM); + if (Aflag >= 1) + warning("missing prototype for `%s'\n", id); + for (i = 0; i < n; i++) + proto[i] = caller[i]->type; + proto[i] = NULL; + ty = func(rty, proto, 1); + } + } else { + callee = params; + caller = newarray(n + 1, sizeof *caller, FUNC); + for (i = 0; (p = callee[i]) != NULL && p->name; i++) { + NEW(caller[i], FUNC); + *caller[i] = *p; + if (isint(p->type)) + caller[i]->type = promote(p->type); + caller[i]->sclass = AUTO; + if ('1' <= *p->name && *p->name <= '9') + error("missing name for parameter %d to function `%s'\n", i + 1, id); + + } + caller[i] = NULL; + } + for (i = 0; (p = callee[i]) != NULL; i++) + if (p->type->size == 0) { + error("undefined size for parameter `%t %s'\n", + p->type, p->name); + caller[i]->type = p->type = inttype; + } + if (Aflag >= 2 && sclass != STATIC && strcmp(id, "main") == 0) { + if (ty->u.f.oldstyle) + warning("`%t %s()' is a non-ANSI definition\n", rty, id); + else if (!(rty == inttype + && ((n == 0 && callee[0] == NULL) + || (n == 2 && callee[0]->type == inttype + && isptr(callee[1]->type) && callee[1]->type->type == charptype + && !variadic(ty))))) + warning("`%s' is a non-ANSI definition\n", typestring(ty, id)); + } + p = lookup(id, identifiers); + if (p && isfunc(p->type) && p->defined) + error("redefinition of `%s' previously defined at %w\n", + p->name, &p->src); + cfunc = dclglobal(sclass, id, ty, &pt); + cfunc->u.f.label = genlabel(1); + cfunc->u.f.callee = callee; + cfunc->u.f.pt = src; + cfunc->defined = 1; + if (xref) + use(cfunc, cfunc->src); + if (Pflag) + printproto(cfunc, cfunc->u.f.callee); + if (ncalled >= 0) + ncalled = findfunc(cfunc->name, pt.file); + labels = table(NULL, LABELS); + stmtlabs = table(NULL, LABELS); + refinc = 1.0; + regcount = 0; + codelist = &codehead; + codelist->next = NULL; + if (!IR->wants_callb && isstruct(rty)) + retv = genident(AUTO, ptr(rty), PARAM); + compound(0, NULL, 0); + + { + Code cp; + for (cp = codelist; cp->kind < Label; cp = cp->prev) + ; + if (cp->kind != Jump) { + if (rty != voidtype) { + warning("missing return value\n"); + retcode(cnsttree(inttype, 0L)); + } else + retcode(NULL); + } + } + definelab(cfunc->u.f.label); + if (events.exit) + apply(events.exit, cfunc, NULL); + walk(NULL, 0, 0); + exitscope(); + assert(level == PARAM); + foreach(identifiers, level, checkref, NULL); + if (!IR->wants_callb && isstruct(rty)) { + Symbol *a; + a = newarray(n + 2, sizeof *a, FUNC); + a[0] = retv; + memcpy(&a[1], callee, (n+1)*sizeof *callee); + callee = a; + a = newarray(n + 2, sizeof *a, FUNC); + NEW(a[0], FUNC); + *a[0] = *retv; + memcpy(&a[1], caller, (n+1)*sizeof *callee); + caller = a; + } + if (!IR->wants_argb) + for (i = 0; caller[i]; i++) + if (isstruct(caller[i]->type)) { + caller[i]->type = ptr(caller[i]->type); + callee[i]->type = ptr(callee[i]->type); + caller[i]->structarg = callee[i]->structarg = 1; + } + if (glevel > 1) for (i = 0; callee[i]; i++) callee[i]->sclass = AUTO; + if (cfunc->sclass != STATIC) + (*IR->export)(cfunc); + if (glevel && IR->stabsym) { + swtoseg(CODE); (*IR->stabsym)(cfunc); } + swtoseg(CODE); + (*IR->function)(cfunc, caller, callee, cfunc->u.f.ncalls); + if (glevel && IR->stabfend) + (*IR->stabfend)(cfunc, lineno); + foreach(stmtlabs, LABELS, checklab, NULL); + exitscope(); + expect('}'); + labels = stmtlabs = NULL; + retv = NULL; + cfunc = NULL; +} +static void oldparam(Symbol p, void *cl) { + int i; + Symbol *callee = cl; + + for (i = 0; callee[i]; i++) + if (p->name == callee[i]->name) { + callee[i] = p; + return; + } + error("declared parameter `%s' is missing\n", p->name); +} +void compound(int loop, struct swtch *swp, int lev) { + Code cp; + int nregs; + + walk(NULL, 0, 0); + cp = code(Blockbeg); + enterscope(); + assert(level >= LOCAL); + if (level == LOCAL && events.entry) + apply(events.entry, cfunc, NULL); + definept(NULL); + expect('{'); + autos = registers = NULL; + if (level == LOCAL && IR->wants_callb + && isstruct(freturn(cfunc->type))) { + retv = genident(AUTO, ptr(freturn(cfunc->type)), level); + retv->defined = 1; + retv->ref = 1; + registers = append(retv, registers); + } + while (kind[t] == CHAR || kind[t] == STATIC + || (istypename(t, tsym) && getchr() != ':')) + decl(dcllocal); + { + int i; + Symbol *a = ltov(&autos, STMT); + nregs = length(registers); + for (i = 0; a[i]; i++) + registers = append(a[i], registers); + cp->u.block.locals = ltov(®isters, FUNC); + } + if (events.blockentry) + apply(events.blockentry, cp->u.block.locals, NULL); + while (kind[t] == IF || kind[t] == ID) + statement(loop, swp, lev); + walk(NULL, 0, 0); + foreach(identifiers, level, checkref, NULL); + { + int i = nregs, j; + Symbol p; + for ( ; (p = cp->u.block.locals[i]) != NULL; i++) { + for (j = i; j > nregs + && cp->u.block.locals[j-1]->ref < p->ref; j--) + cp->u.block.locals[j] = cp->u.block.locals[j-1]; + cp->u.block.locals[j] = p; + } + } + if (events.blockexit) + apply(events.blockexit, cp->u.block.locals, NULL); + cp->u.block.level = level; + cp->u.block.identifiers = identifiers; + cp->u.block.types = types; + code(Blockend)->u.begin = cp; + if (reachable(Gen)) + definept(NULL); + if (level > LOCAL) { + exitscope(); + expect('}'); + } +} +static void checkref(Symbol p, void *cl) { + if (p->scope >= PARAM + && (isvolatile(p->type) || isfunc(p->type))) + p->addressed = 1; + if (Aflag >= 2 && p->defined && p->ref == 0) { + if (p->sclass == STATIC) + warning("static `%t %s' is not referenced\n", + p->type, p->name); + else if (p->scope == PARAM) + warning("parameter `%t %s' is not referenced\n", + p->type, p->name); + else if (p->scope >= LOCAL && p->sclass != EXTERN) + warning("local `%t %s' is not referenced\n", + p->type, p->name); + } + if (p->sclass == AUTO + && ((p->scope == PARAM && regcount == 0) + || p->scope >= LOCAL) + && !p->addressed && isscalar(p->type) && p->ref >= 3.0) + p->sclass = REGISTER; + if (level == GLOBAL && p->sclass == STATIC && !p->defined + && isfunc(p->type) && p->ref) + error("undefined static `%t %s'\n", p->type, p->name); + assert(!(level == GLOBAL && p->sclass == STATIC && !p->defined && !isfunc(p->type))); +} +static Symbol dcllocal(int sclass, char *id, Type ty, Coordinate *pos) { + Symbol p, q; + + if (sclass == 0) + sclass = isfunc(ty) ? EXTERN : AUTO; + else if (isfunc(ty) && sclass != EXTERN) { + error("invalid storage class `%k' for `%t %s'\n", + sclass, ty, id); + sclass = EXTERN; + } else if (sclass == REGISTER + && (isvolatile(ty) || isstruct(ty) || isarray(ty))) { + warning("register declaration ignored for `%t %s'\n", + ty, id); + sclass = AUTO; + } + q = lookup(id, identifiers); + if ((q && q->scope >= level) + || (q && q->scope == PARAM && level == LOCAL)) { + if (sclass == EXTERN && q->sclass == EXTERN + && eqtype(q->type, ty, 1)) + ty = compose(ty, q->type); + else + error("redeclaration of `%s' previously declared at %w\n", q->name, &q->src); + } + + assert(level >= LOCAL); + p = install(id, &identifiers, level, sclass == STATIC || sclass == EXTERN ? PERM : FUNC); + p->type = ty; + p->sclass = sclass; + p->src = *pos; + switch (sclass) { + case EXTERN: q = lookup(id, globals); + if (q == NULL || q->sclass == TYPEDEF || q->sclass == ENUM) { + q = lookup(id, externals); + if (q == NULL) { + q = install(p->name, &externals, GLOBAL, PERM); + q->type = p->type; + q->sclass = EXTERN; + q->src = src; + (*IR->defsymbol)(q); + } + } + if (!eqtype(p->type, q->type, 1)) + warning("declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src); + + p->u.alias = q; break; + case STATIC: (*IR->defsymbol)(p); + initglobal(p, 0); + if (!p->defined) { + if (p->type->size > 0) { + defglobal(p, BSS); + (*IR->space)(p->type->size); + } else + error("undefined size for `%t %s'\n", + p->type, p->name); + } + p->defined = 1; break; + case REGISTER: registers = append(p, registers); + regcount++; + p->defined = 1; + break; + case AUTO: autos = append(p, autos); + p->defined = 1; break; + default: assert(0); + } + if (t == '=') { + Tree e; + if (sclass == EXTERN) + error("illegal initialization of `extern %s'\n", id); + t = gettok(); + definept(NULL); + if (isscalar(p->type) + || (isstruct(p->type) && t != '{')) { + if (t == '{') { + t = gettok(); + e = expr1(0); + expect('}'); + } else + e = expr1(0); + } else { + Symbol t1; + Type ty = p->type, ty1 = ty; + while (isarray(ty1)) + ty1 = ty1->type; + if (!isconst(ty) && (!isarray(ty) || !isconst(ty1))) + ty = qual(CONST, ty); + t1 = genident(STATIC, ty, GLOBAL); + initglobal(t1, 1); + if (isarray(p->type) && p->type->size == 0 + && t1->type->size > 0) + p->type = array(p->type->type, + t1->type->size/t1->type->type->size, 0); + e = idtree(t1); + } + walk(root(asgn(p, e)), 0, 0); + p->ref = 1; + } + if (!isfunc(p->type) && p->defined && p->type->size <= 0) + error("undefined size for `%t %s'\n", p->type, id); + return p; +} +void finalize(void) { + foreach(externals, GLOBAL, doextern, NULL); + foreach(identifiers, GLOBAL, doglobal, NULL); + foreach(identifiers, GLOBAL, checkref, NULL); + foreach(constants, CONSTANTS, doconst, NULL); +} +static void doextern(Symbol p, void *cl) { + (*IR->import)(p); +} +static void doglobal(Symbol p, void *cl) { + if (!p->defined && (p->sclass == EXTERN + || (isfunc(p->type) && p->sclass == AUTO))) + (*IR->import)(p); + else if (!p->defined && !isfunc(p->type) + && (p->sclass == AUTO || p->sclass == STATIC)) { + if (isarray(p->type) + && p->type->size == 0 && p->type->type->size > 0) + p->type = array(p->type->type, 1, 0); + if (p->type->size > 0) { + defglobal(p, BSS); + (*IR->space)(p->type->size); + if (glevel > 0 && IR->stabsym) + (*IR->stabsym)(p); + } else + error("undefined size for `%t %s'\n", + p->type, p->name); + p->defined = 1; + } + if (Pflag + && !isfunc(p->type) + && !p->generated && p->sclass != EXTERN) + printdecl(p, p->type); +} +void doconst(Symbol p, void *cl) { + if (p->u.c.loc) { + assert(p->u.c.loc->u.seg == 0); + defglobal(p->u.c.loc, LIT); + if (isarray(p->type) && p->type->type == widechar) { + unsigned int *s = p->u.c.v.p; + int n = p->type->size/widechar->size; + while (n-- > 0) { + Value v; + v.u = *s++; + (*IR->defconst)(widechar->op, widechar->size, v); + } + } else if (isarray(p->type)) + (*IR->defstring)(p->type->size, p->u.c.v.p); + else + (*IR->defconst)(p->type->op, p->type->size, p->u.c.v); + p->u.c.loc = NULL; + } +} +void checklab(Symbol p, void *cl) { + if (!p->defined) + error("undefined label `%s'\n", p->name); + p->defined = 1; +} + +Type enumdcl(void) { + char *tag; + Type ty; + Symbol p = {0}; + Coordinate pos; + + t = gettok(); + pos = src; + if (t == ID) { + tag = token; + t = gettok(); + } else + tag = ""; + if (t == '{') { + static char follow[] = { IF, 0 }; + int n = 0; + long k = -1; + List idlist = 0; + ty = newstruct(ENUM, tag); + t = gettok(); + if (t != ID) + error("expecting an enumerator identifier\n"); + while (t == ID) { + char *id = token; + Coordinate s; + if (tsym && tsym->scope == level) + error("redeclaration of `%s' previously declared at %w\n", + token, &tsym->src); + s = src; + t = gettok(); + if (t == '=') { + t = gettok(); + k = intexpr(0, 0); + } else { + if (k == inttype->u.sym->u.limits.max.i) + error("overflow in value for enumeration constant `%s'\n", id); + k++; + } + p = install(id, &identifiers, level, level < LOCAL ? PERM : FUNC); + p->src = s; + p->type = ty; + p->sclass = ENUM; + p->u.value = k; + idlist = append(p, idlist); + n++; + if (Aflag >= 2 && n == 128) + warning("more than 127 enumeration constants in `%t'\n", ty); + if (t != ',') + break; + t = gettok(); + if (Aflag >= 2 && t == '}') + warning("non-ANSI trailing comma in enumerator list\n"); + } + test('}', follow); + ty->type = inttype; + ty->size = ty->type->size; + ty->align = ty->type->align; + ty->u.sym->u.idlist = ltov(&idlist, PERM); + ty->u.sym->defined = 1; + } else if ((p = lookup(tag, types)) != NULL && p->type->op == ENUM) { + ty = p->type; + if (t == ';') + error("empty declaration\n"); + } else { + error("unknown enumeration `%s'\n", tag); + ty = newstruct(ENUM, tag); + ty->type = inttype; + } + if (*tag && xref) + use(p, pos); + return ty; +} + +Type typename(void) { + Type ty = specifier(NULL); + + if (t == '*' || t == '(' || t == '[') { + ty = dclr(ty, NULL, NULL, 1); + if (Aflag >= 1 && !hasproto(ty)) + warning("missing prototype\n"); + } + return ty; +} + diff --git a/src/tools/lcc/src/enode.c b/src/tools/lcc/src/enode.c new file mode 100644 index 0000000..760096d --- /dev/null +++ b/src/tools/lcc/src/enode.c @@ -0,0 +1,545 @@ +#include "c.h" + + +static Tree addtree(int, Tree, Tree); +static Tree andtree(int, Tree, Tree); +static Tree cmptree(int, Tree, Tree); +static int compatible(Type, Type); +static int isnullptr(Tree e); +static Tree multree(int, Tree, Tree); +static Tree subtree(int, Tree, Tree); +#define isvoidptr(ty) \ + (isptr(ty) && unqual(ty->type) == voidtype) + +Tree (*optree[])(int, Tree, Tree) = { +#define xx(a,b,c,d,e,f,g) e, +#define yy(a,b,c,d,e,f,g) e, +#include "token.h" +}; +Tree call(Tree f, Type fty, Coordinate src) { + int n = 0; + Tree args = NULL, r = NULL, e; + Type *proto, rty = unqual(freturn(fty)); + Symbol t3 = NULL; + + if (fty->u.f.oldstyle) + proto = NULL; + else + proto = fty->u.f.proto; + if (hascall(f)) + r = f; + if (isstruct(rty)) + { + t3 = temporary(AUTO, unqual(rty)); + if (rty->size == 0) + error("illegal use of incomplete type `%t'\n", rty); + } + if (t != ')') + for (;;) { + Tree q = pointer(expr1(0)); + if (proto && *proto && *proto != voidtype) + { + Type aty; + q = value(q); + aty = assign(*proto, q); + if (aty) + q = cast(q, aty); + else + error("type error in argument %d to %s; found `%t' expected `%t'\n", n + 1, funcname(f), + + q->type, *proto); + if ((isint(q->type) || isenum(q->type)) + && q->type->size != inttype->size) + q = cast(q, promote(q->type)); + ++proto; + } + else + { + if (!fty->u.f.oldstyle && *proto == NULL) + error("too many arguments to %s\n", funcname(f)); + q = value(q); + if (isarray(q->type) || q->type->size == 0) + error("type error in argument %d to %s; `%t' is illegal\n", n + 1, funcname(f), q->type); + + else + q = cast(q, promote(q->type)); + } + if (!IR->wants_argb && isstruct(q->type)) { + if (iscallb(q)) + q = addrof(q); + else { + Symbol t1 = temporary(AUTO, unqual(q->type)); + q = asgn(t1, q); + q = tree(RIGHT, ptr(t1->type), + root(q), lvalue(idtree(t1))); + } + } + if (q->type->size == 0) + q->type = inttype; + if (hascall(q)) + r = r ? tree(RIGHT, voidtype, r, q) : q; + args = tree(mkop(ARG, q->type), q->type, q, args); + n++; + if (Aflag >= 2 && n == 32) + warning("more than 31 arguments in a call to %s\n", + funcname(f)); + if (t != ',') + break; + t = gettok(); + } + expect(')'); + if (proto && *proto && *proto != voidtype) + error("insufficient number of arguments to %s\n", + funcname(f)); + if (r) + args = tree(RIGHT, voidtype, r, args); + e = calltree(f, rty, args, t3); + if (events.calls) + apply(events.calls, &src, &e); + return e; +} +Tree calltree(Tree f, Type ty, Tree args, Symbol t3) { + Tree p; + + if (args) + f = tree(RIGHT, f->type, args, f); + if (isstruct(ty)) + assert(t3), + p = tree(RIGHT, ty, + tree(CALL+B, ty, f, addrof(idtree(t3))), + idtree(t3)); + else { + Type rty = ty; + if (isenum(ty)) + rty = unqual(ty)->type; + if (!isfloat(rty)) + rty = promote(rty); + p = tree(mkop(CALL, rty), rty, f, NULL); + if (isptr(ty) || p->type->size > ty->size) + p = cast(p, ty); + } + return p; +} +Tree vcall(Symbol func, Type ty, ...) { + va_list ap; + Tree args = NULL, e, f = pointer(idtree(func)), r = NULL; + + assert(isfunc(func->type)); + if (ty == NULL) + ty = freturn(func->type); + va_start(ap, ty); + while ((e = va_arg(ap, Tree)) != NULL) { + if (hascall(e)) + r = r == NULL ? e : tree(RIGHT, voidtype, r, e); + args = tree(mkop(ARG, e->type), e->type, e, args); + } + va_end(ap); + if (r != NULL) + args = tree(RIGHT, voidtype, r, args); + return calltree(f, ty, args, NULL); +} +int iscallb(Tree e) { + return e->op == RIGHT && e->kids[0] && e->kids[1] + && e->kids[0]->op == CALL+B + && e->kids[1]->op == INDIR+B + && isaddrop(e->kids[1]->kids[0]->op) + && e->kids[1]->kids[0]->u.sym->temporary; +} + +static Tree addtree(int op, Tree l, Tree r) { + Type ty = inttype; + + if (isarith(l->type) && isarith(r->type)) { + ty = binary(l->type, r->type); + l = cast(l, ty); + r = cast(r, ty); + } else if (isptr(l->type) && isint(r->type)) + return addtree(ADD, r, l); + else if ( isptr(r->type) && isint(l->type) + && !isfunc(r->type->type)) + { + long n; + ty = unqual(r->type); + n = unqual(ty->type)->size; + if (n == 0) + error("unknown size for type `%t'\n", ty->type); + l = cast(l, promote(l->type)); + if (n > 1) + l = multree(MUL, cnsttree(signedptr, n), l); + if (YYcheck && !isaddrop(r->op)) /* omit */ + return nullcall(ty, YYcheck, r, l); /* omit */ + return simplify(ADD, ty, l, r); + } + + else + typeerror(op, l, r); + return simplify(op, ty, l, r); +} + +Tree cnsttree(Type ty, ...) { + Tree p = tree(mkop(CNST,ty), ty, NULL, NULL); + va_list ap; + + va_start(ap, ty); + switch (ty->op) { + case INT: p->u.v.i = va_arg(ap, long); break; + case UNSIGNED:p->u.v.u = va_arg(ap, unsigned long)&ones(8*ty->size); break; + case FLOAT: p->u.v.d = va_arg(ap, double); break; + case POINTER: p->u.v.p = va_arg(ap, void *); break; + default: assert(0); + } + va_end(ap); + return p; +} + +Tree consttree(unsigned n, Type ty) { + if (isarray(ty)) + ty = atop(ty); + else assert(isint(ty)); + return cnsttree(ty, (unsigned long)n); +} +static Tree cmptree(int op, Tree l, Tree r) { + Type ty; + + if (isarith(l->type) && isarith(r->type)) { + ty = binary(l->type, r->type); + l = cast(l, ty); + r = cast(r, ty); + } else if (compatible(l->type, r->type)) { + ty = unsignedptr; + l = cast(l, ty); + r = cast(r, ty); + } else { + ty = unsignedtype; + typeerror(op, l, r); + } + return simplify(mkop(op,ty), inttype, l, r); +} +static int compatible(Type ty1, Type ty2) { + return isptr(ty1) && !isfunc(ty1->type) + && isptr(ty2) && !isfunc(ty2->type) + && eqtype(unqual(ty1->type), unqual(ty2->type), 0); +} +static int isnullptr(Tree e) { + Type ty = unqual(e->type); + + return generic(e->op) == CNST + && ((ty->op == INT && e->u.v.i == 0) + || (ty->op == UNSIGNED && e->u.v.u == 0) + || (isvoidptr(ty) && e->u.v.p == NULL)); +} +Tree eqtree(int op, Tree l, Tree r) { + Type xty = l->type, yty = r->type; + + if ((isptr(xty) && isnullptr(r)) + || (isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)) + || (isptr(xty) && isptr(yty) + && eqtype(unqual(xty->type), unqual(yty->type), 1))) { + Type ty = unsignedptr; + l = cast(l, ty); + r = cast(r, ty); + return simplify(mkop(op,ty), inttype, l, r); + } + if ((isptr(yty) && isnullptr(l)) + || (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))) + return eqtree(op, r, l); + return cmptree(op, l, r); +} + +Type assign(Type xty, Tree e) { + Type yty = unqual(e->type); + + xty = unqual(xty); + if (isenum(xty)) + xty = xty->type; + if (xty->size == 0 || yty->size == 0) + return NULL; + if ( (isarith(xty) && isarith(yty)) + || (isstruct(xty) && xty == yty)) + return xty; + if (isptr(xty) && isnullptr(e)) + return xty; + if (((isvoidptr(xty) && isptr(yty)) + || (isptr(xty) && isvoidptr(yty))) + && ( (isconst(xty->type) || !isconst(yty->type)) + && (isvolatile(xty->type) || !isvolatile(yty->type)))) + return xty; + + if ((isptr(xty) && isptr(yty) + && eqtype(unqual(xty->type), unqual(yty->type), 1)) + && ( (isconst(xty->type) || !isconst(yty->type)) + && (isvolatile(xty->type) || !isvolatile(yty->type)))) + return xty; + if (isptr(xty) && isptr(yty) + && ( (isconst(xty->type) || !isconst(yty->type)) + && (isvolatile(xty->type) || !isvolatile(yty->type)))) { + Type lty = unqual(xty->type), rty = unqual(yty->type); + if ((isenum(lty) && rty == inttype) + || (isenum(rty) && lty == inttype)) { + if (Aflag >= 1) + warning("assignment between `%t' and `%t' is compiler-dependent\n", + xty, yty); + return xty; + } + } + return NULL; +} +Tree asgntree(int op, Tree l, Tree r) { + Type aty, ty; + + r = pointer(r); + ty = assign(l->type, r); + if (ty) + r = cast(r, ty); + else { + typeerror(ASGN, l, r); + if (r->type == voidtype) + r = retype(r, inttype); + ty = r->type; + } + if (l->op != FIELD) + l = lvalue(l); + aty = l->type; + if (isptr(aty)) + aty = unqual(aty)->type; + if ( isconst(aty) + || (isstruct(aty) && unqual(aty)->u.sym->u.s.cfields)) { + if (isaddrop(l->op) + && !l->u.sym->computed && !l->u.sym->generated) + error("assignment to const identifier `%s'\n", + l->u.sym->name); + else + error("assignment to const location\n"); + } + if (l->op == FIELD) { + long n = 8*l->u.field->type->size - fieldsize(l->u.field); + if (n > 0 && isunsigned(l->u.field->type)) + r = bittree(BAND, r, + cnsttree(r->type, (unsigned long)fieldmask(l->u.field))); + else if (n > 0) { + if (r->op == CNST+I) { + n = r->u.v.i; + if (n&(1<<(fieldsize(l->u.field)-1))) + n |= ~0UL<<fieldsize(l->u.field); + r = cnsttree(r->type, n); + } else + r = shtree(RSH, + shtree(LSH, r, cnsttree(inttype, n)), + cnsttree(inttype, n)); + } + } + if (isstruct(ty) && isaddrop(l->op) && iscallb(r)) + return tree(RIGHT, ty, + tree(CALL+B, ty, r->kids[0]->kids[0], l), + idtree(l->u.sym)); + return tree(mkop(op,ty), ty, l, r); +} +Tree condtree(Tree e, Tree l, Tree r) { + Symbol t1; + Type ty, xty = l->type, yty = r->type; + Tree p; + + if (isarith(xty) && isarith(yty)) + ty = binary(xty, yty); + else if (eqtype(xty, yty, 1)) + ty = unqual(xty); + else if (isptr(xty) && isnullptr(r)) + ty = xty; + else if (isnullptr(l) && isptr(yty)) + ty = yty; + else if ((isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)) + || (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))) + ty = voidptype; + else if ((isptr(xty) && isptr(yty) + && eqtype(unqual(xty->type), unqual(yty->type), 1))) + ty = xty; + else { + typeerror(COND, l, r); + return consttree(0, inttype); + } + if (isptr(ty)) { + ty = unqual(unqual(ty)->type); + if ((isptr(xty) && isconst(unqual(xty)->type)) + || (isptr(yty) && isconst(unqual(yty)->type))) + ty = qual(CONST, ty); + if ((isptr(xty) && isvolatile(unqual(xty)->type)) + || (isptr(yty) && isvolatile(unqual(yty)->type))) + ty = qual(VOLATILE, ty); + ty = ptr(ty); + } + switch (e->op) { + case CNST+I: return cast(e->u.v.i != 0 ? l : r, ty); + case CNST+U: return cast(e->u.v.u != 0 ? l : r, ty); + case CNST+P: return cast(e->u.v.p != 0 ? l : r, ty); + case CNST+F: return cast(e->u.v.d != 0.0 ? l : r, ty); + } + if (ty != voidtype && ty->size > 0) { + t1 = genident(REGISTER, unqual(ty), level); + /* t1 = temporary(REGISTER, unqual(ty)); */ + l = asgn(t1, l); + r = asgn(t1, r); + } else + t1 = NULL; + p = tree(COND, ty, cond(e), + tree(RIGHT, ty, root(l), root(r))); + p->u.sym = t1; + return p; +} +/* addrof - address of p */ +Tree addrof(Tree p) { + Tree q = p; + + for (;;) + switch (generic(q->op)) { + case RIGHT: + assert(q->kids[0] || q->kids[1]); + q = q->kids[1] ? q->kids[1] : q->kids[0]; + continue; + case ASGN: + q = q->kids[1]; + continue; + case COND: { + Symbol t1 = q->u.sym; + q->u.sym = 0; + q = idtree(t1); + /* fall thru */ + } + case INDIR: + if (p == q) + return q->kids[0]; + q = q->kids[0]; + return tree(RIGHT, q->type, root(p), q); + default: + error("addressable object required\n"); + return value(p); + } +} + +/* andtree - construct tree for l [&& ||] r */ +static Tree andtree(int op, Tree l, Tree r) { + if (!isscalar(l->type) || !isscalar(r->type)) + typeerror(op, l, r); + return simplify(op, inttype, cond(l), cond(r)); +} + +/* asgn - generate tree for assignment of expr e to symbol p sans qualifiers */ +Tree asgn(Symbol p, Tree e) { + if (isarray(p->type)) + e = tree(ASGN+B, p->type, idtree(p), + tree(INDIR+B, e->type, e, NULL)); + else { + Type ty = p->type; + p->type = unqual(p->type); + if (isstruct(p->type) && p->type->u.sym->u.s.cfields) { + p->type->u.sym->u.s.cfields = 0; + e = asgntree(ASGN, idtree(p), e); + p->type->u.sym->u.s.cfields = 1; + } else + e = asgntree(ASGN, idtree(p), e); + p->type = ty; + } + return e; +} + +/* bittree - construct tree for l [& | ^ %] r */ +Tree bittree(int op, Tree l, Tree r) { + Type ty = inttype; + + if (isint(l->type) && isint(r->type)) { + ty = binary(l->type, r->type); + l = cast(l, ty); + r = cast(r, ty); + } else + typeerror(op, l, r); + return simplify(op, ty, l, r); +} + +/* multree - construct tree for l [* /] r */ +static Tree multree(int op, Tree l, Tree r) { + Type ty = inttype; + + if (isarith(l->type) && isarith(r->type)) { + ty = binary(l->type, r->type); + l = cast(l, ty); + r = cast(r, ty); + } else + typeerror(op, l, r); + return simplify(op, ty, l, r); +} + +/* shtree - construct tree for l [>> <<] r */ +Tree shtree(int op, Tree l, Tree r) { + Type ty = inttype; + + if (isint(l->type) && isint(r->type)) { + ty = promote(l->type); + l = cast(l, ty); + r = cast(r, inttype); + } else + typeerror(op, l, r); + return simplify(op, ty, l, r); +} + +/* subtree - construct tree for l - r */ +static Tree subtree(int op, Tree l, Tree r) { + long n; + Type ty = inttype; + + if (isarith(l->type) && isarith(r->type)) { + ty = binary(l->type, r->type); + l = cast(l, ty); + r = cast(r, ty); + } else if (isptr(l->type) && !isfunc(l->type->type) && isint(r->type)) { + ty = unqual(l->type); + n = unqual(ty->type)->size; + if (n == 0) + error("unknown size for type `%t'\n", ty->type); + r = cast(r, promote(r->type)); + if (n > 1) + r = multree(MUL, cnsttree(signedptr, n), r); + if (isunsigned(r->type)) + r = cast(r, unsignedptr); + else + r = cast(r, signedptr); + return simplify(SUB+P, ty, l, r); + } else if (compatible(l->type, r->type)) { + ty = unqual(l->type); + n = unqual(ty->type)->size; + if (n == 0) + error("unknown size for type `%t'\n", ty->type); + l = simplify(SUB+U, unsignedptr, + cast(l, unsignedptr), cast(r, unsignedptr)); + return simplify(DIV+I, longtype, + cast(l, longtype), cnsttree(longtype, n)); + } else + typeerror(op, l, r); + return simplify(op, ty, l, r); +} + +/* typeerror - issue "operands of op have illegal types `l' and `r'" */ +void typeerror(int op, Tree l, Tree r) { + int i; + static struct { int op; char *name; } ops[] = { + {ASGN, "="}, {INDIR, "*"}, {NEG, "-"}, + {ADD, "+"}, {SUB, "-"}, {LSH, "<<"}, + {MOD, "%"}, {RSH, ">>"}, {BAND, "&"}, + {BCOM, "~"}, {BOR, "|"}, {BXOR, "^"}, + {DIV, "/"}, {MUL, "*"}, {EQ, "=="}, + {GE, ">="}, {GT, ">"}, {LE, "<="}, + {LT, "<"}, {NE, "!="}, {AND, "&&"}, + {NOT, "!"}, {OR, "||"}, {COND, "?:"}, + {0, 0} + }; + + op = generic(op); + for (i = 0; ops[i].op; i++) + if (op == ops[i].op) + break; + assert(ops[i].name); + if (r) + error("operands of %s have illegal types `%t' and `%t'\n", + ops[i].name, l->type, r->type); + else + error("operand of unary %s has illegal type `%t'\n", ops[i].name, + l->type); +} diff --git a/src/tools/lcc/src/error.c b/src/tools/lcc/src/error.c new file mode 100644 index 0000000..2187c10 --- /dev/null +++ b/src/tools/lcc/src/error.c @@ -0,0 +1,137 @@ +#include "c.h" + + +static void printtoken(void); +int errcnt = 0; +int errlimit = 20; +char kind[] = { +#define xx(a,b,c,d,e,f,g) f, +#define yy(a,b,c,d,e,f,g) f, +#include "token.h" +}; +int wflag; /* != 0 to suppress warning messages */ + +void test(int tok, char set[]) { + if (t == tok) + t = gettok(); + else { + expect(tok); + skipto(tok, set); + if (t == tok) + t = gettok(); + } +} +void expect(int tok) { + if (t == tok) + t = gettok(); + else { + error("syntax error; found"); + printtoken(); + fprint(stderr, " expecting `%k'\n", tok); + } +} +void error(const char *fmt, ...) { + va_list ap; + + if (errcnt++ >= errlimit) { + errcnt = -1; + error("too many errors\n"); + exit(1); + } + va_start(ap, fmt); + if (firstfile != file && firstfile && *firstfile) + fprint(stderr, "%s: ", firstfile); + fprint(stderr, "%w: ", &src); + vfprint(stderr, NULL, fmt, ap); + va_end(ap); +} + +void skipto(int tok, char set[]) { + int n; + char *s; + + assert(set); + for (n = 0; t != EOI && t != tok; t = gettok()) { + for (s = set; *s && kind[t] != *s; s++) + ; + if (kind[t] == *s) + break; + if (n++ == 0) + error("skipping"); + if (n <= 8) + printtoken(); + else if (n == 9) + fprint(stderr, " ..."); + } + if (n > 8) { + fprint(stderr, " up to"); + printtoken(); + } + if (n > 0) + fprint(stderr, "\n"); +} +/* fatal - issue fatal error message and exit */ +int fatal(const char *name, const char *fmt, int n) { + print("\n"); + errcnt = -1; + error("compiler error in %s--", name); + fprint(stderr, fmt, n); + exit(EXIT_FAILURE); + return 0; +} + +/* printtoken - print current token preceeded by a space */ +static void printtoken(void) { + switch (t) { + case ID: fprint(stderr, " `%s'", token); break; + case ICON: + fprint(stderr, " `%s'", vtoa(tsym->type, tsym->u.c.v)); + break; + case SCON: { + int i, n; + if (ischar(tsym->type->type)) { + char *s = tsym->u.c.v.p; + n = tsym->type->size; + fprint(stderr, " \""); + for (i = 0; i < 20 && i < n && *s; s++, i++) + if (*s < ' ' || *s >= 0177) + fprint(stderr, "\\%o", *s); + else + fprint(stderr, "%c", *s); + } else { /* wchar_t string */ + unsigned int *s = tsym->u.c.v.p; + assert(tsym->type->type->size == widechar->size); + n = tsym->type->size/widechar->size; + fprint(stderr, " L\""); + for (i = 0; i < 20 && i < n && *s; s++, i++) + if (*s < ' ' || *s >= 0177) + fprint(stderr, "\\x%x", *s); + else + fprint(stderr, "%c", *s); + } + if (i < n) + fprint(stderr, " ..."); + else + fprint(stderr, "\""); + break; + } + case FCON: + fprint(stderr, " `%S'", token, (char*)cp - token); + break; + case '`': case '\'': fprint(stderr, " \"%k\"", t); break; + default: fprint(stderr, " `%k'", t); + } +} + +/* warning - issue warning error message */ +void warning(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (wflag == 0) { + errcnt--; + error("warning: "); + vfprint(stderr, NULL, fmt, ap); + } + va_end(ap); +} diff --git a/src/tools/lcc/src/event.c b/src/tools/lcc/src/event.c new file mode 100644 index 0000000..4549e3f --- /dev/null +++ b/src/tools/lcc/src/event.c @@ -0,0 +1,28 @@ +#include "c.h" + + +struct entry { + Apply func; + void *cl; +}; + +Events events; +void attach(Apply func, void *cl, List *list) { + struct entry *p; + + NEW(p, PERM); + p->func = func; + p->cl = cl; + *list = append(p, *list); +} +void apply(List event, void *arg1, void *arg2) { + if (event) { + List lp = event; + do { + struct entry *p = lp->x; + (*p->func)(p->cl, arg1, arg2); + lp = lp->link; + } while (lp != event); + } +} + diff --git a/src/tools/lcc/src/expr.c b/src/tools/lcc/src/expr.c new file mode 100644 index 0000000..b8cb08b --- /dev/null +++ b/src/tools/lcc/src/expr.c @@ -0,0 +1,711 @@ +#include "c.h" + + +static char prec[] = { +#define xx(a,b,c,d,e,f,g) c, +#define yy(a,b,c,d,e,f,g) c, +#include "token.h" +}; +static int oper[] = { +#define xx(a,b,c,d,e,f,g) d, +#define yy(a,b,c,d,e,f,g) d, +#include "token.h" +}; +float refinc = 1.0; +static Tree expr2(void); +static Tree expr3(int); +static Tree nullcheck(Tree); +static Tree postfix(Tree); +static Tree unary(void); +static Tree primary(void); +static Type super(Type ty); + +static Type super(Type ty) { + switch (ty->op) { + case INT: + if (ty->size < inttype->size) + return inttype; + break; + case UNSIGNED: + if (ty->size < unsignedtype->size) + return unsignedtype; + break; + case POINTER: + return unsignedptr; + } + return ty; +} +Tree expr(int tok) { + static char stop[] = { IF, ID, '}', 0 }; + Tree p = expr1(0); + + while (t == ',') { + Tree q; + t = gettok(); + q = pointer(expr1(0)); + p = tree(RIGHT, q->type, root(value(p)), q); + } + if (tok) + test(tok, stop); + return p; +} +Tree expr0(int tok) { + return root(expr(tok)); +} +Tree expr1(int tok) { + static char stop[] = { IF, ID, 0 }; + Tree p = expr2(); + + if (t == '=' + || (prec[t] >= 6 && prec[t] <= 8) + || (prec[t] >= 11 && prec[t] <= 13)) { + int op = t; + t = gettok(); + if (oper[op] == ASGN) + p = asgntree(ASGN, p, value(expr1(0))); + else + { + expect('='); + p = incr(op, p, expr1(0)); + } + } + if (tok) + test(tok, stop); + return p; +} +Tree incr(int op, Tree v, Tree e) { + return asgntree(ASGN, v, (*optree[op])(oper[op], v, e)); +} +static Tree expr2(void) { + Tree p = expr3(4); + + if (t == '?') { + Tree l, r; + Coordinate pts[2]; + if (Aflag > 1 && isfunc(p->type)) + warning("%s used in a conditional expression\n", + funcname(p)); + p = pointer(p); + t = gettok(); + pts[0] = src; + l = pointer(expr(':')); + pts[1] = src; + r = pointer(expr2()); + if (events.points) + { + apply(events.points, &pts[0], &l); + apply(events.points, &pts[1], &r); + } + p = condtree(p, l, r); + } + return p; +} +Tree value(Tree p) { + int op = generic(rightkid(p)->op); + + if (p->type != voidtype + && (op==AND || op==OR || op==NOT || op==EQ || op==NE + || op== LE || op==LT || op== GE || op==GT)) + p = condtree(p, consttree(1, inttype), + consttree(0, inttype)); + return p; +} +static Tree expr3(int k) { + int k1; + Tree p = unary(); + + for (k1 = prec[t]; k1 >= k; k1--) + while (prec[t] == k1 && *cp != '=') { + Tree r; + Coordinate pt; + int op = t; + t = gettok(); + pt = src; + p = pointer(p); + if (op == ANDAND || op == OROR) { + r = pointer(expr3(k1)); + if (events.points) + apply(events.points, &pt, &r); + } else + r = pointer(expr3(k1 + 1)); + p = (*optree[op])(oper[op], p, r); + } + return p; +} +static Tree unary(void) { + Tree p; + + switch (t) { + case '*': t = gettok(); p = unary(); p = pointer(p); + if (isptr(p->type) + && (isfunc(p->type->type) || isarray(p->type->type))) + p = retype(p, p->type->type); + else { + if (YYnull) + p = nullcheck(p); + p = rvalue(p); + } break; + case '&': t = gettok(); p = unary(); if (isarray(p->type) || isfunc(p->type)) + p = retype(p, ptr(p->type)); + else + p = lvalue(p); + if (isaddrop(p->op) && p->u.sym->sclass == REGISTER) + error("invalid operand of unary &; `%s' is declared register\n", p->u.sym->name); + + else if (isaddrop(p->op)) + p->u.sym->addressed = 1; + break; + case '+': t = gettok(); p = unary(); p = pointer(p); + if (isarith(p->type)) + p = cast(p, promote(p->type)); + else + typeerror(ADD, p, NULL); break; + case '-': t = gettok(); p = unary(); p = pointer(p); + if (isarith(p->type)) { + Type ty = promote(p->type); + p = cast(p, ty); + if (isunsigned(ty)) { + warning("unsigned operand of unary -\n"); + p = simplify(ADD, ty, simplify(BCOM, ty, p, NULL), cnsttree(ty, 1UL)); + } else + p = simplify(NEG, ty, p, NULL); + } else + typeerror(SUB, p, NULL); break; + case '~': t = gettok(); p = unary(); p = pointer(p); + if (isint(p->type)) { + Type ty = promote(p->type); + p = simplify(BCOM, ty, cast(p, ty), NULL); + } else + typeerror(BCOM, p, NULL); break; + case '!': t = gettok(); p = unary(); p = pointer(p); + if (isscalar(p->type)) + p = simplify(NOT, inttype, cond(p), NULL); + else + typeerror(NOT, p, NULL); break; + case INCR: t = gettok(); p = unary(); p = incr(INCR, pointer(p), consttree(1, inttype)); break; + case DECR: t = gettok(); p = unary(); p = incr(DECR, pointer(p), consttree(1, inttype)); break; + case TYPECODE: case SIZEOF: { int op = t; + Type ty; + p = NULL; + t = gettok(); + if (t == '(') { + t = gettok(); + if (istypename(t, tsym)) { + ty = typename(); + expect(')'); + } else { + p = postfix(expr(')')); + ty = p->type; + } + } else { + p = unary(); + ty = p->type; + } + assert(ty); + if (op == TYPECODE) + p = cnsttree(inttype, (long)ty->op); + else { + if (isfunc(ty) || ty->size == 0) + error("invalid type argument `%t' to `sizeof'\n", ty); + else if (p && rightkid(p)->op == FIELD) + error("`sizeof' applied to a bit field\n"); + p = cnsttree(unsignedlong, (unsigned long)ty->size); + } } break; + case '(': + t = gettok(); + if (istypename(t, tsym)) { + Type ty, ty1 = typename(), pty; + expect(')'); + ty = unqual(ty1); + if (isenum(ty)) { + Type ty2 = ty->type; + if (isconst(ty1)) + ty2 = qual(CONST, ty2); + if (isvolatile(ty1)) + ty2 = qual(VOLATILE, ty2); + ty1 = ty2; + ty = ty->type; + } + p = pointer(unary()); + pty = p->type; + if (isenum(pty)) + pty = pty->type; + if ((isarith(pty) && isarith(ty)) + || (isptr(pty) && isptr(ty))) { + explicitCast++; + p = cast(p, ty); + explicitCast--; + } else if ((isptr(pty) && isint(ty)) + || (isint(pty) && isptr(ty))) { + if (Aflag >= 1 && ty->size < pty->size) + warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, ty); + + p = cast(p, ty); + } else if (ty != voidtype) { + error("cast from `%t' to `%t' is illegal\n", + p->type, ty1); + ty1 = inttype; + } + if (generic(p->op) == INDIR || ty->size == 0) + p = tree(RIGHT, ty1, NULL, p); + else + p = retype(p, ty1); + } else + p = postfix(expr(')')); + break; + default: + p = postfix(primary()); + } + return p; +} + +static Tree postfix(Tree p) { + for (;;) + switch (t) { + case INCR: p = tree(RIGHT, p->type, + tree(RIGHT, p->type, + p, + incr(t, p, consttree(1, inttype))), + p); + t = gettok(); break; + case DECR: p = tree(RIGHT, p->type, + tree(RIGHT, p->type, + p, + incr(t, p, consttree(1, inttype))), + p); + t = gettok(); break; + case '[': { + Tree q; + t = gettok(); + q = expr(']'); + if (YYnull) { + if (isptr(p->type)) + p = nullcheck(p); + else if (isptr(q->type)) + q = nullcheck(q); + } + p = (*optree['+'])(ADD, pointer(p), pointer(q)); + if (isptr(p->type) && isarray(p->type->type)) + p = retype(p, p->type->type); + else + p = rvalue(p); + } break; + case '(': { + Type ty; + Coordinate pt; + p = pointer(p); + if (isptr(p->type) && isfunc(p->type->type)) + ty = p->type->type; + else { + error("found `%t' expected a function\n", p->type); + ty = func(voidtype, NULL, 1); + p = retype(p, ptr(ty)); + } + pt = src; + t = gettok(); + p = call(p, ty, pt); + } break; + case '.': t = gettok(); + if (t == ID) { + if (isstruct(p->type)) { + Tree q = addrof(p); + p = field(q, token); + q = rightkid(q); + if (isaddrop(q->op) && q->u.sym->temporary) + p = tree(RIGHT, p->type, p, NULL); + } else + error("left operand of . has incompatible type `%t'\n", + p->type); + t = gettok(); + } else + error("field name expected\n"); break; + case DEREF: t = gettok(); + p = pointer(p); + if (t == ID) { + if (isptr(p->type) && isstruct(p->type->type)) { + if (YYnull) + p = nullcheck(p); + p = field(p, token); + } else + error("left operand of -> has incompatible type `%t'\n", p->type); + + t = gettok(); + } else + error("field name expected\n"); break; + default: + return p; + } +} +static Tree primary(void) { + Tree p; + + assert(t != '('); + switch (t) { + case ICON: + case FCON: p = tree(mkop(CNST,tsym->type), tsym->type, NULL, NULL); + p->u.v = tsym->u.c.v; + break; + case SCON: if (ischar(tsym->type->type)) + tsym->u.c.v.p = stringn(tsym->u.c.v.p, tsym->type->size); + else + tsym->u.c.v.p = memcpy(allocate(tsym->type->size, PERM), tsym->u.c.v.p, tsym->type->size); + tsym = constant(tsym->type, tsym->u.c.v); + if (tsym->u.c.loc == NULL) + tsym->u.c.loc = genident(STATIC, tsym->type, GLOBAL); + p = idtree(tsym->u.c.loc); break; + case ID: if (tsym == NULL) + { + Symbol p = install(token, &identifiers, level, FUNC); + p->src = src; + if (getchr() == '(') { + Symbol q = lookup(token, externals); + p->type = func(inttype, NULL, 1); + p->sclass = EXTERN; + if (Aflag >= 1) + warning("missing prototype\n"); + if (q && !eqtype(q->type, p->type, 1)) + warning("implicit declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src); + + if (q == NULL) { + q = install(p->name, &externals, GLOBAL, PERM); + q->type = p->type; + q->sclass = EXTERN; + q->src = src; + (*IR->defsymbol)(q); + } + p->u.alias = q; + } else { + error("undeclared identifier `%s'\n", p->name); + p->sclass = AUTO; + p->type = inttype; + if (p->scope == GLOBAL) + (*IR->defsymbol)(p); + else + addlocal(p); + } + t = gettok(); + if (xref) + use(p, src); + return idtree(p); + } + if (xref) + use(tsym, src); + if (tsym->sclass == ENUM) + p = consttree(tsym->u.value, inttype); + else { + if (tsym->sclass == TYPEDEF) + error("illegal use of type name `%s'\n", tsym->name); + p = idtree(tsym); + } break; + case FIRSTARG: + if (level > PARAM && cfunc && cfunc->u.f.callee[0]) + p = idtree(cfunc->u.f.callee[0]); + else { + error("illegal use of `%k'\n", FIRSTARG); + p = cnsttree(inttype, 0L); + } + break; + default: + error("illegal expression\n"); + p = cnsttree(inttype, 0L); + } + t = gettok(); + return p; +} +Tree idtree(Symbol p) { + int op; + Tree e; + Type ty = p->type ? unqual(p->type) : voidptype; + + if (p->scope == GLOBAL || p->sclass == STATIC) + op = ADDRG; + else if (p->scope == PARAM) { + op = ADDRF; + if (isstruct(p->type) && !IR->wants_argb) + { + e = tree(mkop(op,voidptype), ptr(ptr(p->type)), NULL, NULL); + e->u.sym = p; + return rvalue(rvalue(e)); + } + } else if (p->sclass == EXTERN) { + assert(p->u.alias); + p = p->u.alias; + op = ADDRG; + } else + op = ADDRL; + p->ref += refinc; + if (isarray(ty)) + e = tree(mkop(op,voidptype), p->type, NULL, NULL); + else if (isfunc(ty)) + e = tree(mkop(op,funcptype), p->type, NULL, NULL); + else + e = tree(mkop(op,voidptype), ptr(p->type), NULL, NULL); + e->u.sym = p; + if (isptr(e->type)) + e = rvalue(e); + return e; +} + +Tree rvalue(Tree p) { + Type ty = deref(p->type); + + ty = unqual(ty); + return tree(mkop(INDIR,ty), ty, p, NULL); +} +Tree lvalue(Tree p) { + if (generic(p->op) != INDIR) { + error("lvalue required\n"); + return value(p); + } else if (unqual(p->type) == voidtype) + warning("`%t' used as an lvalue\n", p->type); + return p->kids[0]; +} +Tree retype(Tree p, Type ty) { + Tree q; + + if (p->type == ty) + return p; + q = tree(p->op, ty, p->kids[0], p->kids[1]); + q->node = p->node; + q->u = p->u; + return q; +} +Tree rightkid(Tree p) { + while (p && p->op == RIGHT) + if (p->kids[1]) + p = p->kids[1]; + else if (p->kids[0]) + p = p->kids[0]; + else + assert(0); + assert(p); + return p; +} +int hascall(Tree p) { + if (p == 0) + return 0; + if (generic(p->op) == CALL || (IR->mulops_calls && + (p->op == DIV+I || p->op == MOD+I || p->op == MUL+I + || p->op == DIV+U || p->op == MOD+U || p->op == MUL+U))) + return 1; + return hascall(p->kids[0]) || hascall(p->kids[1]); +} +Type binary(Type xty, Type yty) { +#define xx(t) if (xty == t || yty == t) return t + xx(longdouble); + xx(doubletype); + xx(floattype); + xx(unsignedlonglong); + xx(longlong); + xx(unsignedlong); + if ((xty == longtype && yty == unsignedtype) + || (xty == unsignedtype && yty == longtype)) { + if (longtype->size > unsignedtype->size) + return longtype; + else + return unsignedlong; + } + xx(longtype); + xx(unsignedtype); + return inttype; +#undef xx +} +Tree pointer(Tree p) { + if (isarray(p->type)) + /* assert(p->op != RIGHT || p->u.sym == NULL), */ + p = retype(p, atop(p->type)); + else if (isfunc(p->type)) + p = retype(p, ptr(p->type)); + return p; +} +Tree cond(Tree p) { + int op = generic(rightkid(p)->op); + + if (op == AND || op == OR || op == NOT + || op == EQ || op == NE + || op == LE || op == LT || op == GE || op == GT) + return p; + p = pointer(p); + return (*optree[NEQ])(NE, p, consttree(0, inttype)); +} +Tree cast(Tree p, Type type) { + Type src, dst; + + p = value(p); + if (p->type == type) + return p; + dst = unqual(type); + src = unqual(p->type); + if (src->op != dst->op || src->size != dst->size) { + switch (src->op) { + case INT: + if (src->size < inttype->size) + p = simplify(CVI, inttype, p, NULL); + break; + case UNSIGNED: + if (src->size < inttype->size) + p = simplify(CVU, inttype, p, NULL); + else if (src->size < unsignedtype->size) + p = simplify(CVU, unsignedtype, p, NULL); + break; + case ENUM: + p = retype(p, inttype); + break; + case POINTER: + if (isint(dst) && src->size > dst->size) + warning("conversion from `%t' to `%t' is undefined\n", p->type, type); + p = simplify(CVP, super(src), p, NULL); + break; + case FLOAT: + break; + default: assert(0); + } + { + src = unqual(p->type); + dst = super(dst); + if (src->op != dst->op) + switch (src->op) { + case INT: + p = simplify(CVI, dst, p, NULL); + break; + case UNSIGNED: + if (isfloat(dst)) { + Type ssrc = signedint(src); + Tree two = cnsttree(longdouble, (double)2.0); + p = (*optree['+'])(ADD, + (*optree['*'])(MUL, + two, + simplify(CVU, ssrc, + simplify(RSH, src, + p, consttree(1, inttype)), NULL)), + simplify(CVU, ssrc, + simplify(BAND, src, + p, consttree(1, unsignedtype)), NULL)); + } else + p = simplify(CVU, dst, p, NULL); + break; + case FLOAT: + if (isunsigned(dst)) { + Type sdst = signedint(dst); + Tree c = cast(cnsttree(longdouble, (double)sdst->u.sym->u.limits.max.i + 1), src); + p = condtree( + simplify(GE, src, p, c), + (*optree['+'])(ADD, + cast(cast(simplify(SUB, src, p, c), sdst), dst), + cast(cnsttree(unsignedlong, (unsigned long)sdst->u.sym->u.limits.max.i + 1), dst)), + simplify(CVF, sdst, p, NULL)); + } else + p = simplify(CVF, dst, p, NULL); + break; + default: assert(0); + } + dst = unqual(type); + } + } + src = unqual(p->type); + switch (src->op) { + case INT: + if (src->op != dst->op || src->size != dst->size) + p = simplify(CVI, dst, p, NULL); + break; + case UNSIGNED: + if (src->op != dst->op || src->size != dst->size) + p = simplify(CVU, dst, p, NULL); + break; + case FLOAT: + if (src->op != dst->op || src->size != dst->size) + p = simplify(CVF, dst, p, NULL); + break; + case POINTER: + if (src->op != dst->op) + p = simplify(CVP, dst, p, NULL); + else { + if ((isfunc(src->type) && !isfunc(dst->type)) + || (!isfunc(src->type) && isfunc(dst->type))) + warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, type); + + if (src->size != dst->size) + p = simplify(CVP, dst, p, NULL); + } + break; + default: assert(0); + } + return retype(p, type); +} +Tree field(Tree p, const char *name) { + Field q; + Type ty1, ty = p->type; + + if (isptr(ty)) + ty = deref(ty); + ty1 = ty; + ty = unqual(ty); + if ((q = fieldref(name, ty)) != NULL) { + if (isarray(q->type)) { + ty = q->type->type; + if (isconst(ty1) && !isconst(ty)) + ty = qual(CONST, ty); + if (isvolatile(ty1) && !isvolatile(ty)) + ty = qual(VOLATILE, ty); + ty = array(ty, q->type->size/ty->size, q->type->align); + } else { + ty = q->type; + if (isconst(ty1) && !isconst(ty)) + ty = qual(CONST, ty); + if (isvolatile(ty1) && !isvolatile(ty)) + ty = qual(VOLATILE, ty); + ty = ptr(ty); + } + if (YYcheck && !isaddrop(p->op) && q->offset > 0) /* omit */ + p = nullcall(ty, YYcheck, p, consttree(q->offset, inttype)); /* omit */ + else /* omit */ + p = simplify(ADD+P, ty, p, consttree(q->offset, inttype)); + + if (q->lsb) { + p = tree(FIELD, ty->type, rvalue(p), NULL); + p->u.field = q; + } else if (!isarray(q->type)) + p = rvalue(p); + + } else { + error("unknown field `%s' of `%t'\n", name, ty); + p = rvalue(retype(p, ptr(inttype))); + } + return p; +} +/* funcname - return name of function f or a function' */ +char *funcname(Tree f) { + if (isaddrop(f->op)) + return stringf("`%s'", f->u.sym->name); + return "a function"; +} +static Tree nullcheck(Tree p) { + if (!needconst && YYnull && isptr(p->type)) { + p = value(p); + if (strcmp(YYnull->name, "_YYnull") == 0) { + Symbol t1 = temporary(REGISTER, voidptype); + p = tree(RIGHT, p->type, + tree(OR, voidtype, + cond(asgn(t1, cast(p, voidptype))), + vcall(YYnull, voidtype, (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL)), + idtree(t1)); + } + + else + p = nullcall(p->type, YYnull, p, cnsttree(inttype, 0L)); + + } + return p; +} +Tree nullcall(Type pty, Symbol f, Tree p, Tree e) { + Type ty; + + if (isarray(pty)) + return retype(nullcall(atop(pty), f, p, e), pty); + ty = unqual(unqual(p->type)->type); + return vcall(f, pty, + p, e, + cnsttree(inttype, (long)ty->size), + cnsttree(inttype, (long)ty->align), + (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL); +} diff --git a/src/tools/lcc/src/gen.c b/src/tools/lcc/src/gen.c new file mode 100644 index 0000000..4ee170d --- /dev/null +++ b/src/tools/lcc/src/gen.c @@ -0,0 +1,830 @@ +#include "c.h" + + +#define readsreg(p) \ + (generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P) +#define setsrc(d) ((d) && (d)->x.regnode && \ + (d)->x.regnode->set == src->x.regnode->set && \ + (d)->x.regnode->mask&src->x.regnode->mask) + +#define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b)) + +static Symbol askfixedreg(Symbol); +static Symbol askreg(Symbol, unsigned*); +static void blkunroll(int, int, int, int, int, int, int[]); +static void docall(Node); +static void dumpcover(Node, int, int); +static void dumpregs(char *, char *, char *); +static void dumprule(int); +static void dumptree(Node); +static unsigned emitasm(Node, int); +static void genreload(Node, Symbol, int); +static void genspill(Symbol, Node, Symbol); +static Symbol getreg(Symbol, unsigned*, Node); +static int getrule(Node, int); +static void linearize(Node, Node); +static int moveself(Node); +static void prelabel(Node); +static Node* prune(Node, Node*); +static void putreg(Symbol); +static void ralloc(Node); +static void reduce(Node, int); +static int reprune(Node*, int, int, Node); +static int requate(Node); +static Node reuse(Node, int); +static void rewrite(Node); +static Symbol spillee(Symbol, unsigned mask[], Node); +static void spillr(Symbol, Node); +static int uses(Node, Regnode); + +int offset; + +int maxoffset; + +int framesize; +int argoffset; + +int maxargoffset; + +int dalign, salign; +int bflag = 0; /* omit */ +int dflag = 0; + +int swap; + +unsigned (*emitter)(Node, int) = emitasm; +static char NeedsReg[] = { + 0, /* unused */ + 1, /* CNST */ + 0, 0, /* ARG ASGN */ + 1, /* INDIR */ + 0, 0, 1, 1, /* - - CVF CVI */ + 1, 0, 1, 1, /* CVP - CVU NEG */ + 1, /* CALL */ + 1, /* LOAD */ + 0, /* RET */ + 1, 1, 1, /* ADDRG ADDRF ADDRL */ + 1, 1, 1, 1, 1, /* ADD SUB LSH MOD RSH */ + 1, 1, 1, 1, /* BAND BCOM BOR BXOR */ + 1, 1, /* DIV MUL */ + 0, 0, 0, 0, 0, 0, /* EQ GE GT LE LT NE */ + 0, 0 /* JUMP LABEL */ +}; +Node head; + +unsigned freemask[2]; +unsigned usedmask[2]; +unsigned tmask[2]; +unsigned vmask[2]; +Symbol mkreg(char *fmt, int n, int mask, int set) { + Symbol p; + + NEW0(p, PERM); + p->name = p->x.name = stringf(fmt, n); + NEW0(p->x.regnode, PERM); + p->x.regnode->number = n; + p->x.regnode->mask = mask<<n; + p->x.regnode->set = set; + return p; +} +Symbol mkwildcard(Symbol *syms) { + Symbol p; + + NEW0(p, PERM); + p->name = p->x.name = "wildcard"; + p->x.wildcard = syms; + return p; +} +void mkauto(Symbol p) { + assert(p->sclass == AUTO); + offset = roundup(offset + p->type->size, p->type->align); + p->x.offset = -offset; + p->x.name = stringd(-offset); +} +void blockbeg(Env *e) { + e->offset = offset; + e->freemask[IREG] = freemask[IREG]; + e->freemask[FREG] = freemask[FREG]; +} +void blockend(Env *e) { + if (offset > maxoffset) + maxoffset = offset; + offset = e->offset; + freemask[IREG] = e->freemask[IREG]; + freemask[FREG] = e->freemask[FREG]; +} +int mkactual(int align, int size) { + int n = roundup(argoffset, align); + + argoffset = n + size; + return n; +} +static void docall(Node p) { + p->syms[1] = p->syms[0]; + p->syms[0] = intconst(argoffset); + if (argoffset > maxargoffset) + maxargoffset = argoffset; + argoffset = 0; +} +void blkcopy(int dreg, int doff, int sreg, int soff, int size, int tmp[]) { + assert(size >= 0); + if (size == 0) + return; + else if (size <= 2) + blkunroll(size, dreg, doff, sreg, soff, size, tmp); + else if (size == 3) { + blkunroll(2, dreg, doff, sreg, soff, 2, tmp); + blkunroll(1, dreg, doff+2, sreg, soff+2, 1, tmp); + } + else if (size <= 16) { + blkunroll(4, dreg, doff, sreg, soff, size&~3, tmp); + blkcopy(dreg, doff+(size&~3), + sreg, soff+(size&~3), size&3, tmp); + } + else + (*IR->x.blkloop)(dreg, doff, sreg, soff, size, tmp); +} +static void blkunroll(int k, int dreg, int doff, int sreg, int soff, int size, int tmp[]) { + int i; + + assert(IR->x.max_unaligned_load); + if (k > IR->x.max_unaligned_load + && (k > salign || k > dalign)) + k = IR->x.max_unaligned_load; + for (i = 0; i+k < size; i += 2*k) { + (*IR->x.blkfetch)(k, soff+i, sreg, tmp[0]); + (*IR->x.blkfetch)(k, soff+i+k, sreg, tmp[1]); + (*IR->x.blkstore)(k, doff+i, dreg, tmp[0]); + (*IR->x.blkstore)(k, doff+i+k, dreg, tmp[1]); + } + if (i < size) { + (*IR->x.blkfetch)(k, i+soff, sreg, tmp[0]); + (*IR->x.blkstore)(k, i+doff, dreg, tmp[0]); + } +} +void parseflags(int argc, char *argv[]) { + int i; + + for (i = 0; i < argc; i++) + if (strcmp(argv[i], "-d") == 0) + dflag = 1; + else if (strcmp(argv[i], "-b") == 0) /* omit */ + bflag = 1; /* omit */ +} +static int getrule(Node p, int nt) { + int rulenum; + + assert(p); + rulenum = (*IR->x._rule)(p->x.state, nt); + if (!rulenum) { + fprint(stderr, "(%x->op=%s at %w is corrupt.)\n", p, opname(p->op), &src); + assert(0); + } + return rulenum; +} +static void reduce(Node p, int nt) { + int rulenum, i; + short *nts; + Node kids[10]; + + p = reuse(p, nt); + rulenum = getrule(p, nt); + nts = IR->x._nts[rulenum]; + (*IR->x._kids)(p, rulenum, kids); + for (i = 0; nts[i]; i++) + reduce(kids[i], nts[i]); + if (IR->x._isinstruction[rulenum]) { + assert(p->x.inst == 0 || p->x.inst == nt); + p->x.inst = nt; + if (p->syms[RX] && p->syms[RX]->temporary) { + debug(fprint(stderr, "(using %s)\n", p->syms[RX]->name)); + p->syms[RX]->x.usecount++; + } + } +} +static Node reuse(Node p, int nt) { + struct _state { + short cost[1]; + }; + Symbol r = p->syms[RX]; + + if (generic(p->op) == INDIR && p->kids[0]->op == VREG+P + && r->u.t.cse && p->x.mayrecalc + && ((struct _state*)r->u.t.cse->x.state)->cost[nt] == 0) + return r->u.t.cse; + else + return p; +} + +int mayrecalc(Node p) { + int op; + + assert(p && p->syms[RX]); + if (p->syms[RX]->u.t.cse == NULL) + return 0; + op = generic(p->syms[RX]->u.t.cse->op); + if (op == CNST || op == ADDRF || op == ADDRG || op == ADDRL) { + p->x.mayrecalc = 1; + return 1; + } else + return 0; +} +static Node *prune(Node p, Node pp[]) { + if (p == NULL) + return pp; + p->x.kids[0] = p->x.kids[1] = p->x.kids[2] = NULL; + if (p->x.inst == 0) + return prune(p->kids[1], prune(p->kids[0], pp)); + else if (p->syms[RX] && p->syms[RX]->temporary + && p->syms[RX]->x.usecount < 2) { + p->x.inst = 0; + debug(fprint(stderr, "(clobbering %s)\n", p->syms[RX]->name)); + return prune(p->kids[1], prune(p->kids[0], pp)); + } + else { + prune(p->kids[1], prune(p->kids[0], &p->x.kids[0])); + *pp = p; + return pp + 1; + } +} + +#define ck(i) return (i) ? 0 : LBURG_MAX + +int range(Node p, int lo, int hi) { + Symbol s = p->syms[0]; + + switch (specific(p->op)) { + case ADDRF+P: + case ADDRL+P: ck(s->x.offset >= lo && s->x.offset <= hi); + case CNST+I: ck(s->u.c.v.i >= lo && s->u.c.v.i <= hi); + case CNST+U: ck(s->u.c.v.u >= lo && s->u.c.v.u <= hi); + case CNST+P: ck(s->u.c.v.p == 0 && lo <= 0 && hi >= 0); + } + return LBURG_MAX; +} +static void dumptree(Node p) { + if (p->op == VREG+P && p->syms[0]) { + fprint(stderr, "VREGP(%s)", p->syms[0]->name); + return; + } else if (generic(p->op) == LOAD) { + fprint(stderr, "LOAD("); + dumptree(p->kids[0]); + fprint(stderr, ")"); + return; + } + fprint(stderr, "%s(", opname(p->op)); + switch (generic(p->op)) { + case CNST: case LABEL: + case ADDRG: case ADDRF: case ADDRL: + if (p->syms[0]) + fprint(stderr, "%s", p->syms[0]->name); + break; + case RET: + if (p->kids[0]) + dumptree(p->kids[0]); + break; + case CVF: case CVI: case CVP: case CVU: case JUMP: + case ARG: case BCOM: case NEG: case INDIR: + dumptree(p->kids[0]); + break; + case CALL: + if (optype(p->op) != B) { + dumptree(p->kids[0]); + break; + } + /* else fall thru */ + case EQ: case NE: case GT: case GE: case LE: case LT: + case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH: + case ADD: case SUB: case DIV: case MUL: case MOD: + dumptree(p->kids[0]); + fprint(stderr, ", "); + dumptree(p->kids[1]); + break; + default: assert(0); + } + fprint(stderr, ")"); +} +static void dumpcover(Node p, int nt, int in) { + int rulenum, i; + short *nts; + Node kids[10]; + + p = reuse(p, nt); + rulenum = getrule(p, nt); + nts = IR->x._nts[rulenum]; + fprint(stderr, "dumpcover(%x) = ", p); + for (i = 0; i < in; i++) + fprint(stderr, " "); + dumprule(rulenum); + (*IR->x._kids)(p, rulenum, kids); + for (i = 0; nts[i]; i++) + dumpcover(kids[i], nts[i], in+1); +} + +static void dumprule(int rulenum) { + assert(rulenum); + fprint(stderr, "%s / %s", IR->x._string[rulenum], + IR->x._templates[rulenum]); + if (!IR->x._isinstruction[rulenum]) + fprint(stderr, "\n"); +} +static unsigned emitasm(Node p, int nt) { + int rulenum; + short *nts; + char *fmt; + Node kids[10]; + + p = reuse(p, nt); + rulenum = getrule(p, nt); + nts = IR->x._nts[rulenum]; + fmt = IR->x._templates[rulenum]; + assert(fmt); + if (IR->x._isinstruction[rulenum] && p->x.emitted) + print("%s", p->syms[RX]->x.name); + else if (*fmt == '#') + (*IR->x.emit2)(p); + else { + if (*fmt == '?') { + fmt++; + assert(p->kids[0]); + if (p->syms[RX] == p->x.kids[0]->syms[RX]) + while (*fmt++ != '\n') + ; + } + for ((*IR->x._kids)(p, rulenum, kids); *fmt; fmt++) + if (*fmt != '%') + (void)putchar(*fmt); + else if (*++fmt == 'F') + print("%d", framesize); + else if (*fmt >= '0' && *fmt <= '9') + emitasm(kids[*fmt - '0'], nts[*fmt - '0']); + else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms)) + fputs(p->syms[*fmt - 'a']->x.name, stdout); + else + (void)putchar(*fmt); + } + return 0; +} +void emit(Node p) { + for (; p; p = p->x.next) { + assert(p->x.registered); + if ((p->x.equatable && requate(p)) || moveself(p)) + ; + else + (*emitter)(p, p->x.inst); + p->x.emitted = 1; + } +} +static int moveself(Node p) { + return p->x.copy + && p->syms[RX]->x.name == p->x.kids[0]->syms[RX]->x.name; +} +int move(Node p) { + p->x.copy = 1; + return 1; +} +static int requate(Node q) { + Symbol src = q->x.kids[0]->syms[RX]; + Symbol tmp = q->syms[RX]; + Node p; + int n = 0; + + debug(fprint(stderr, "(requate(%x): tmp=%s src=%s)\n", q, tmp->x.name, src->x.name)); + for (p = q->x.next; p; p = p->x.next) + if (p->x.copy && p->syms[RX] == src + && p->x.kids[0]->syms[RX] == tmp) + debug(fprint(stderr, "(requate arm 0 at %x)\n", p)), + p->syms[RX] = tmp; + else if (setsrc(p->syms[RX]) && !moveself(p) && !readsreg(p)) + return 0; + else if (p->x.spills) + return 0; + else if (generic(p->op) == CALL && p->x.next) + return 0; + else if (p->op == LABEL+V && p->x.next) + return 0; + else if (p->syms[RX] == tmp && readsreg(p)) + debug(fprint(stderr, "(requate arm 5 at %x)\n", p)), + n++; + else if (p->syms[RX] == tmp) + break; + debug(fprint(stderr, "(requate arm 7 at %x)\n", p)); + assert(n > 0); + for (p = q->x.next; p; p = p->x.next) + if (p->syms[RX] == tmp && readsreg(p)) { + p->syms[RX] = src; + if (--n <= 0) + break; + } + return 1; +} +static void prelabel(Node p) { + if (p == NULL) + return; + prelabel(p->kids[0]); + prelabel(p->kids[1]); + if (NeedsReg[opindex(p->op)]) + setreg(p, (*IR->x.rmap)(opkind(p->op))); + switch (generic(p->op)) { + case ADDRF: case ADDRL: + if (p->syms[0]->sclass == REGISTER) + p->op = VREG+P; + break; + case INDIR: + if (p->kids[0]->op == VREG+P) + setreg(p, p->kids[0]->syms[0]); + break; + case ASGN: + if (p->kids[0]->op == VREG+P) + rtarget(p, 1, p->kids[0]->syms[0]); + break; + case CVI: case CVU: case CVP: + if (optype(p->op) != F + && opsize(p->op) <= p->syms[0]->u.c.v.i) + p->op = LOAD + opkind(p->op); + break; + } + (IR->x.target)(p); +} +void setreg(Node p, Symbol r) { + p->syms[RX] = r; +} +void rtarget(Node p, int n, Symbol r) { + Node q = p->kids[n]; + + assert(q); + assert(r); + assert(r->sclass == REGISTER || !r->x.wildcard); + assert(q->syms[RX]); + if (r != q->syms[RX] && !q->syms[RX]->x.wildcard) { + q = newnode(LOAD + opkind(q->op), + q, NULL, q->syms[0]); + if (r->u.t.cse == p->kids[n]) + r->u.t.cse = q; + p->kids[n] = p->x.kids[n] = q; + q->x.kids[0] = q->kids[0]; + } + setreg(q, r); + debug(fprint(stderr, "(targeting %x->x.kids[%d]=%x to %s)\n", p, n, p->kids[n], r->x.name)); +} +static void rewrite(Node p) { + assert(p->x.inst == 0); + prelabel(p); + debug(dumptree(p)); + debug(fprint(stderr, "\n")); + (*IR->x._label)(p); + debug(dumpcover(p, 1, 0)); + reduce(p, 1); +} +Node gen(Node forest) { + int i; + struct node sentinel; + Node dummy, p; + + head = forest; + for (p = forest; p; p = p->link) { + assert(p->count == 0); + if (generic(p->op) == CALL) + docall(p); + else if ( generic(p->op) == ASGN + && generic(p->kids[1]->op) == CALL) + docall(p->kids[1]); + else if (generic(p->op) == ARG) + (*IR->x.doarg)(p); + rewrite(p); + p->x.listed = 1; + } + for (p = forest; p; p = p->link) + prune(p, &dummy); + relink(&sentinel, &sentinel); + for (p = forest; p; p = p->link) + linearize(p, &sentinel); + forest = sentinel.x.next; + assert(forest); + sentinel.x.next->x.prev = NULL; + sentinel.x.prev->x.next = NULL; + for (p = forest; p; p = p->x.next) + for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { + assert(p->x.kids[i]->syms[RX]); + if (p->x.kids[i]->syms[RX]->temporary) { + p->x.kids[i]->x.prevuse = + p->x.kids[i]->syms[RX]->x.lastuse; + p->x.kids[i]->syms[RX]->x.lastuse = p->x.kids[i]; + } + } + for (p = forest; p; p = p->x.next) { + ralloc(p); + if (p->x.listed && NeedsReg[opindex(p->op)] + && (*IR->x.rmap)(opkind(p->op))) { + assert(generic(p->op) == CALL || generic(p->op) == LOAD); + putreg(p->syms[RX]); + } + } + return forest; +} +int notarget(Node p) { + return p->syms[RX]->x.wildcard ? 0 : LBURG_MAX; +} +static void putreg(Symbol r) { + assert(r && r->x.regnode); + freemask[r->x.regnode->set] |= r->x.regnode->mask; + debug(dumpregs("(freeing %s)\n", r->x.name, NULL)); +} +static Symbol askfixedreg(Symbol s) { + Regnode r = s->x.regnode; + int n = r->set; + + if (r->mask&~freemask[n]) + return NULL; + else { + freemask[n] &= ~r->mask; + usedmask[n] |= r->mask; + return s; + } +} +static Symbol askreg(Symbol rs, unsigned rmask[]) { + int i; + + if (rs->x.wildcard == NULL) + return askfixedreg(rs); + for (i = 31; i >= 0; i--) { + Symbol r = rs->x.wildcard[i]; + if (r != NULL + && !(r->x.regnode->mask&~rmask[r->x.regnode->set]) + && askfixedreg(r)) + return r; + } + return NULL; +} + +static Symbol getreg(Symbol s, unsigned mask[], Node p) { + Symbol r = askreg(s, mask); + if (r == NULL) { + r = spillee(s, mask, p); + assert(r && r->x.regnode); + spill(r->x.regnode->mask, r->x.regnode->set, p); + r = askreg(s, mask); + } + assert(r && r->x.regnode); + r->x.regnode->vbl = NULL; + return r; +} +int askregvar(Symbol p, Symbol regs) { + Symbol r; + + assert(p); + if (p->sclass != REGISTER) + return 0; + else if (!isscalar(p->type)) { + p->sclass = AUTO; + return 0; + } + else if (p->temporary) { + p->x.name = "?"; + return 1; + } + else if ((r = askreg(regs, vmask)) != NULL) { + p->x.regnode = r->x.regnode; + p->x.regnode->vbl = p; + p->x.name = r->x.name; + debug(dumpregs("(allocating %s to symbol %s)\n", p->x.name, p->name)); + return 1; + } + else { + p->sclass = AUTO; + return 0; + } +} +static void linearize(Node p, Node next) { + int i; + + for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) + linearize(p->x.kids[i], next); + relink(next->x.prev, p); + relink(p, next); + debug(fprint(stderr, "(listing %x)\n", p)); +} +static void ralloc(Node p) { + int i; + unsigned mask[2]; + + mask[0] = tmask[0]; + mask[1] = tmask[1]; + assert(p); + debug(fprint(stderr, "(rallocing %x)\n", p)); + for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { + Node kid = p->x.kids[i]; + Symbol r = kid->syms[RX]; + assert(r && kid->x.registered); + if (r->sclass != REGISTER && r->x.lastuse == kid) + putreg(r); + } + if (!p->x.registered && NeedsReg[opindex(p->op)] + && (*IR->x.rmap)(opkind(p->op))) { + Symbol sym = p->syms[RX], set = sym; + assert(sym); + if (sym->temporary) + set = (*IR->x.rmap)(opkind(p->op)); + assert(set); + if (set->sclass != REGISTER) { + Symbol r; + if (*IR->x._templates[getrule(p, p->x.inst)] == '?') + for (i = 1; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { + Symbol r = p->x.kids[i]->syms[RX]; + assert(p->x.kids[i]->x.registered); + assert(r && r->x.regnode); + assert(sym->x.wildcard || sym != r); + mask[r->x.regnode->set] &= ~r->x.regnode->mask; + } + r = getreg(set, mask, p); + if (sym->temporary) { + Node q; + r->x.lastuse = sym->x.lastuse; + for (q = sym->x.lastuse; q; q = q->x.prevuse) { + q->syms[RX] = r; + q->x.registered = 1; + if (sym->u.t.cse && q->x.copy) + q->x.equatable = 1; + } + } else { + p->syms[RX] = r; + r->x.lastuse = p; + } + debug(dumpregs("(allocating %s to node %x)\n", r->x.name, (char *) p)); + } + } + p->x.registered = 1; + (*IR->x.clobber)(p); +} +static Symbol spillee(Symbol set, unsigned mask[], Node here) { + Symbol bestreg = NULL; + int bestdist = -1, i; + + assert(set); + if (!set->x.wildcard) + bestreg = set; + else { + for (i = 31; i >= 0; i--) { + Symbol ri = set->x.wildcard[i]; + if ( + ri != NULL && + ri->x.lastuse && + (ri->x.regnode->mask&tmask[ri->x.regnode->set]&mask[ri->x.regnode->set]) + ) { + Regnode rn = ri->x.regnode; + Node q = here; + int dist = 0; + for (; q && !uses(q, rn); q = q->x.next) + dist++; + if (q && dist > bestdist) { + bestdist = dist; + bestreg = ri; + } + } + } + } + assert(bestreg); /* Must be able to spill something. Reconfigure the register allocator + to ensure that we can allocate a register for all nodes without spilling + the node's necessary input regs. */ + assert(bestreg->x.regnode->vbl == NULL); /* Can't spill register variables because + the reload site might be in other blocks. Reconfigure the register allocator + to ensure that this register is never allocated to a variable. */ + return bestreg; +} +static int uses(Node p, Regnode rn) { + int i; + + for (i = 0; i < NELEMS(p->x.kids); i++) + if ( + p->x.kids[i] && + p->x.kids[i]->x.registered && + rn->set == p->x.kids[i]->syms[RX]->x.regnode->set && + (rn->mask&p->x.kids[i]->syms[RX]->x.regnode->mask) + ) + return 1; + return 0; +} +static void spillr(Symbol r, Node here) { + int i; + Symbol tmp; + Node p = r->x.lastuse; + assert(p); + while (p->x.prevuse) + assert(r == p->syms[RX]), + p = p->x.prevuse; + assert(p->x.registered && !readsreg(p)); + tmp = newtemp(AUTO, optype(p->op), opsize(p->op)); + genspill(r, p, tmp); + for (p = here->x.next; p; p = p->x.next) + for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { + Node k = p->x.kids[i]; + if (k->x.registered && k->syms[RX] == r) + genreload(p, tmp, i); + } + putreg(r); +} +static void genspill(Symbol r, Node last, Symbol tmp) { + Node p, q; + Symbol s; + unsigned ty; + + debug(fprint(stderr, "(spilling %s to local %s)\n", r->x.name, tmp->x.name)); + debug(fprint(stderr, "(genspill: ")); + debug(dumptree(last)); + debug(fprint(stderr, ")\n")); + ty = opkind(last->op); + NEW0(s, FUNC); + s->sclass = REGISTER; + s->name = s->x.name = r->x.name; + s->x.regnode = r->x.regnode; + q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, s); + q = newnode(INDIR + ty, q, NULL, NULL); + p = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp); + p = newnode(ASGN + ty, p, q, NULL); + p->x.spills = 1; + rewrite(p); + prune(p, &q); + q = last->x.next; + linearize(p, q); + for (p = last->x.next; p != q; p = p->x.next) { + ralloc(p); + assert(!p->x.listed || !NeedsReg[opindex(p->op)] || !(*IR->x.rmap)(opkind(p->op))); + } +} + +static void genreload(Node p, Symbol tmp, int i) { + Node q; + int ty; + + debug(fprint(stderr, "(replacing %x with a reload from %s)\n", p->x.kids[i], tmp->x.name)); + debug(fprint(stderr, "(genreload: ")); + debug(dumptree(p->x.kids[i])); + debug(fprint(stderr, ")\n")); + ty = opkind(p->x.kids[i]->op); + q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp); + p->x.kids[i] = newnode(INDIR + ty, q, NULL, NULL); + rewrite(p->x.kids[i]); + prune(p->x.kids[i], &q); + reprune(&p->kids[1], reprune(&p->kids[0], 0, i, p), i, p); + prune(p, &q); + linearize(p->x.kids[i], p); +} +static int reprune(Node *pp, int k, int n, Node p) { + struct node x, *q = *pp; + + if (q == NULL || k > n) + return k; + else if (q->x.inst == 0) + return reprune(&q->kids[1], + reprune(&q->kids[0], k, n, p), n, p); + if (k == n) { + debug(fprint(stderr, "(reprune changes %x from %x to %x)\n", pp, *pp, p->x.kids[n])); + *pp = p->x.kids[n]; + x = *p; + (IR->x.target)(&x); + } + return k + 1; +} +void spill(unsigned mask, int n, Node here) { + int i; + Node p; + + here->x.spills = 1; + usedmask[n] |= mask; + if (mask&~freemask[n]) { + + assert( /* It makes no sense for a node to clobber() its target. */ + here->x.registered == 0 || /* call isn't coming through clobber() */ + here->syms[RX] == NULL || + here->syms[RX]->x.regnode == NULL || + here->syms[RX]->x.regnode->set != n || + (here->syms[RX]->x.regnode->mask&mask) == 0 + ); + + for (p = here; p; p = p->x.next) + for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { + Symbol r = p->x.kids[i]->syms[RX]; + assert(r); + if (p->x.kids[i]->x.registered && r->x.regnode->set == n + && r->x.regnode->mask&mask) + spillr(r, here); + } + } +} +static void dumpregs(char *msg, char *a, char *b) { + fprint(stderr, msg, a, b); + fprint(stderr, "(free[0]=%x)\n", freemask[0]); + fprint(stderr, "(free[1]=%x)\n", freemask[1]); +} + +int getregnum(Node p) { + assert(p && p->syms[RX] && p->syms[RX]->x.regnode); + return p->syms[RX]->x.regnode->number; +} + + +unsigned regloc(Symbol p) { + assert(p && p->sclass == REGISTER && p->sclass == REGISTER && p->x.regnode); + return p->x.regnode->set<<8 | p->x.regnode->number; +} + diff --git a/src/tools/lcc/src/init.c b/src/tools/lcc/src/init.c new file mode 100644 index 0000000..172d7c0 --- /dev/null +++ b/src/tools/lcc/src/init.c @@ -0,0 +1,318 @@ +#include "c.h" + + +static int curseg; /* current segment */ + +/* defpointer - initialize a pointer to p or to 0 if p==0 */ +void defpointer(Symbol p) { + if (p) { + (*IR->defaddress)(p); + p->ref++; + } else { + static Value v; + (*IR->defconst)(P, voidptype->size, v); + } +} + +/* genconst - generate/check constant expression e; return size */ +static int genconst(Tree e, int def) { + for (;;) + switch (generic(e->op)) { + case ADDRG: + if (def) + (*IR->defaddress)(e->u.sym); + return e->type->size; + case CNST: + if (e->op == CNST+P && isarray(e->type)) { + e = cvtconst(e); + continue; + } + if (def) + (*IR->defconst)(e->type->op, e->type->size, e->u.v); + return e->type->size; + case RIGHT: + assert(e->kids[0] || e->kids[1]); + if (e->kids[1] && e->kids[0]) + error("initializer must be constant\n"); + e = e->kids[1] ? e->kids[1] : e->kids[0]; + continue; + case CVP: + if (isarith(e->type)) + error("cast from `%t' to `%t' is illegal in constant expressions\n", + e->kids[0]->type, e->type); + /* fall thru */ + case CVI: case CVU: case CVF: + e = e->kids[0]; + continue; + default: + error("initializer must be constant\n"); + if (def) + genconst(consttree(0, inttype), def); + return inttype->size; + } +} + +/* initvalue - evaluate a constant expression for a value of integer type ty */ +static Tree initvalue(Type ty) { + Type aty; + Tree e; + + needconst++; + e = expr1(0); + if ((aty = assign(ty, e)) != NULL) + e = cast(e, aty); + else { + error("invalid initialization type; found `%t' expected `%t'\n", + e->type, ty); + e = retype(consttree(0, inttype), ty); + } + needconst--; + if (generic(e->op) != CNST) { + error("initializer must be constant\n"); + e = retype(consttree(0, inttype), ty); + } + return e; +} + +/* initarray - initialize array of ty of <= len bytes; if len == 0, go to } */ +static int initarray(int len, Type ty, int lev) { + int n = 0; + + do { + initializer(ty, lev); + n += ty->size; + if ((len > 0 && n >= len) || t != ',') + break; + t = gettok(); + } while (t != '}'); + return n; +} + +/* initchar - initialize array of <= len ty characters; if len == 0, go to } */ +static int initchar(int len, Type ty) { + int n = 0; + char buf[16], *s = buf; + + do { + *s++ = initvalue(ty)->u.v.i; + if (++n%inttype->size == 0) { + (*IR->defstring)(inttype->size, buf); + s = buf; + } + if ((len > 0 && n >= len) || t != ',') + break; + t = gettok(); + } while (t != '}'); + if (s > buf) + (*IR->defstring)(s - buf, buf); + return n; +} + +/* initend - finish off an initialization at level lev; accepts trailing comma */ +static void initend(int lev, char follow[]) { + if (lev == 0 && t == ',') + t = gettok(); + test('}', follow); +} + +/* initfields - initialize <= an unsigned's worth of bit fields in fields p to q */ +static int initfields(Field p, Field q) { + unsigned int bits = 0; + int i, n = 0; + + do { + i = initvalue(inttype)->u.v.i; + if (fieldsize(p) < 8*p->type->size) { + if ((p->type == inttype && + (i < -(int)(fieldmask(p)>>1)-1 || i > (int)(fieldmask(p)>>1))) + || (p->type == unsignedtype && (i&~fieldmask(p)) != 0)) + warning("initializer exceeds bit-field width\n"); + i &= fieldmask(p); + } + bits |= i<<fieldright(p); + if (IR->little_endian) { + if (fieldsize(p) + fieldright(p) > n) + n = fieldsize(p) + fieldright(p); + } else { + if (fieldsize(p) + fieldleft(p) > n) + n = fieldsize(p) + fieldleft(p); + } + if (p->link == q) + break; + p = p->link; + } while (t == ',' && (t = gettok()) != 0); + n = (n + 7)/8; + for (i = 0; i < n; i++) { + Value v; + if (IR->little_endian) { + v.u = (unsigned char)bits; + bits >>= 8; + } else { /* a big endian */ + v.u = (unsigned char)(bits>>(8*(unsignedtype->size - 1))); + bits <<= 8; + } + (*IR->defconst)(U, unsignedchar->size, v); + } + return n; +} + +/* initstruct - initialize a struct ty of <= len bytes; if len == 0, go to } */ +static int initstruct(int len, Type ty, int lev) { + int a, n = 0; + Field p = ty->u.sym->u.s.flist; + + do { + if (p->offset > n) { + (*IR->space)(p->offset - n); + n += p->offset - n; + } + if (p->lsb) { + Field q = p; + while (q->link && q->link->offset == p->offset) + q = q->link; + n += initfields(p, q->link); + p = q; + } else { + initializer(p->type, lev); + n += p->type->size; + } + if (p->link) { + p = p->link; + a = p->type->align; + } else + a = ty->align; + if (a && n%a) { + (*IR->space)(a - n%a); + n = roundup(n, a); + } + if ((len > 0 && n >= len) || t != ',') + break; + t = gettok(); + } while (t != '}'); + return n; +} + +/* initializer - constexpr | { constexpr ( , constexpr )* [ , ] } */ +Type initializer(Type ty, int lev) { + int n = 0; + Tree e; + Type aty = NULL; + static char follow[] = { IF, CHAR, STATIC, 0 }; + + ty = unqual(ty); + if (isscalar(ty)) { + needconst++; + if (t == '{') { + t = gettok(); + e = expr1(0); + initend(lev, follow); + } else + e = expr1(0); + e = pointer(e); + if ((aty = assign(ty, e)) != NULL) + e = cast(e, aty); + else + error("invalid initialization type; found `%t' expected `%t'\n", + e->type, ty); + n = genconst(e, 1); + deallocate(STMT); + needconst--; + } + if ((isunion(ty) || isstruct(ty)) && ty->size == 0) { + static char follow[] = { CHAR, STATIC, 0 }; + error("cannot initialize undefined `%t'\n", ty); + skipto(';', follow); + return ty; + } else if (isunion(ty)) { + if (t == '{') { + t = gettok(); + n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); + initend(lev, follow); + } else { + if (lev == 0) + error("missing { in initialization of `%t'\n", ty); + n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); + } + } else if (isstruct(ty)) { + if (t == '{') { + t = gettok(); + n = initstruct(0, ty, lev + 1); + test('}', follow); + } else if (lev > 0) + n = initstruct(ty->size, ty, lev + 1); + else { + error("missing { in initialization of `%t'\n", ty); + n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); + } + } + if (isarray(ty)) + aty = unqual(ty->type); + if (isarray(ty) && ischar(aty)) { + if (t == SCON) { + if (ty->size > 0 && ty->size == tsym->type->size - 1) + tsym->type = array(chartype, ty->size, 0); + n = tsym->type->size; + (*IR->defstring)(tsym->type->size, tsym->u.c.v.p); + t = gettok(); + } else if (t == '{') { + t = gettok(); + if (t == SCON) { + ty = initializer(ty, lev + 1); + initend(lev, follow); + return ty; + } + n = initchar(0, aty); + test('}', follow); + } else if (lev > 0 && ty->size > 0) + n = initchar(ty->size, aty); + else { /* eg, char c[] = 0; */ + error("missing { in initialization of `%t'\n", ty); + n = initchar(1, aty); + } + } else if (isarray(ty)) { + if (t == SCON && aty == widechar) { + int i; + unsigned int *s = tsym->u.c.v.p; + if (ty->size > 0 && ty->size == tsym->type->size - widechar->size) + tsym->type = array(widechar, ty->size/widechar->size, 0); + n = tsym->type->size; + for (i = 0; i < n; i += widechar->size) { + Value v; + v.u = *s++; + (*IR->defconst)(widechar->op, widechar->size, v); + } + t = gettok(); + } else if (t == '{') { + t = gettok(); + if (t == SCON && aty == widechar) { + ty = initializer(ty, lev + 1); + initend(lev, follow); + return ty; + } + n = initarray(0, aty, lev + 1); + test('}', follow); + } else if (lev > 0 && ty->size > 0) + n = initarray(ty->size, aty, lev + 1); + else { + error("missing { in initialization of `%t'\n", ty); + n = initarray(aty->size, aty, lev + 1); + } + } + if (ty->size) { + if (n > ty->size) + error("too many initializers\n"); + else if (n < ty->size) + (*IR->space)(ty->size - n); + } else if (isarray(ty) && ty->type->size > 0) + ty = array(ty->type, n/ty->type->size, 0); + else + ty->size = n; + return ty; +} + +/* swtoseg - switch to segment seg, if necessary */ +void swtoseg(int seg) { + if (curseg != seg) + (*IR->segment)(seg); + curseg = seg; +} diff --git a/src/tools/lcc/src/inits.c b/src/tools/lcc/src/inits.c new file mode 100644 index 0000000..c42f61e --- /dev/null +++ b/src/tools/lcc/src/inits.c @@ -0,0 +1,7 @@ +void init(int argc, char *argv[]) { + {extern void input_init(int, char *[]); input_init(argc, argv);} + {extern void main_init(int, char *[]); main_init(argc, argv);} + {extern void prof_init(int, char *[]); prof_init(argc, argv);} + {extern void trace_init(int, char *[]); trace_init(argc, argv);} + {extern void type_init(int, char *[]); type_init(argc, argv);} +} diff --git a/src/tools/lcc/src/input.c b/src/tools/lcc/src/input.c new file mode 100644 index 0000000..c2a084e --- /dev/null +++ b/src/tools/lcc/src/input.c @@ -0,0 +1,135 @@ +#include "c.h" + + +static void pragma(void); +static void resynch(void); + +static int bsize; +static unsigned char buffer[MAXLINE+1 + BUFSIZE+1]; +unsigned char *cp; /* current input character */ +char *file; /* current input file name */ +char *firstfile; /* first input file */ +unsigned char *limit; /* points to last character + 1 */ +char *line; /* current line */ +int lineno; /* line number of current line */ + +void nextline(void) { + do { + if (cp >= limit) { + fillbuf(); + if (cp >= limit) + cp = limit; + if (cp == limit) + return; + } else { + lineno++; + for (line = (char *)cp; *cp==' ' || *cp=='\t'; cp++) + ; + if (*cp == '#') { + resynch(); + nextline(); + } + } + } while (*cp == '\n' && cp == limit); +} +void fillbuf(void) { + if (bsize == 0) + return; + if (cp >= limit) + cp = &buffer[MAXLINE+1]; + else + { + int n = limit - cp; + unsigned char *s = &buffer[MAXLINE+1] - n; + assert(s >= buffer); + line = (char *)s - ((char *)cp - line); + while (cp < limit) + *s++ = *cp++; + cp = &buffer[MAXLINE+1] - n; + } + if (feof(stdin)) + bsize = 0; + else + bsize = fread(&buffer[MAXLINE+1], 1, BUFSIZE, stdin); + if (bsize < 0) { + error("read error\n"); + exit(EXIT_FAILURE); + } + limit = &buffer[MAXLINE+1+bsize]; + *limit = '\n'; +} +void input_init(int argc, char *argv[]) { + static int inited; + + if (inited) + return; + inited = 1; + main_init(argc, argv); + limit = cp = &buffer[MAXLINE+1]; + bsize = -1; + lineno = 0; + file = NULL; + fillbuf(); + if (cp >= limit) + cp = limit; + nextline(); +} + +/* pragma - handle #pragma ref id... */ +static void pragma(void) { + if ((t = gettok()) == ID && strcmp(token, "ref") == 0) + for (;;) { + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp == '\n' || *cp == 0) + break; + if ((t = gettok()) == ID && tsym) { + tsym->ref++; + use(tsym, src); + } + } +} + +/* resynch - set line number/file name in # n [ "file" ] and #pragma ... */ +static void resynch(void) { + for (cp++; *cp == ' ' || *cp == '\t'; ) + cp++; + if (limit - cp < MAXLINE) + fillbuf(); + if (strncmp((char *)cp, "pragma", 6) == 0) { + cp += 6; + pragma(); + } else if (*cp >= '0' && *cp <= '9') { + line: for (lineno = 0; *cp >= '0' && *cp <= '9'; ) + lineno = 10*lineno + *cp++ - '0'; + lineno--; + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp == '"') { + file = (char *)++cp; + while (*cp && *cp != '"' && *cp != '\n') + cp++; + file = stringn(file, (char *)cp - file); + if (*cp == '\n') + warning("missing \" in preprocessor line\n"); + if (firstfile == 0) + firstfile = file; + } + } else if (strncmp((char *)cp, "line", 4) == 0) { + for (cp += 4; *cp == ' ' || *cp == '\t'; ) + cp++; + if (*cp >= '0' && *cp <= '9') + goto line; + if (Aflag >= 2) + warning("unrecognized control line\n"); + } else if (Aflag >= 2 && *cp != '\n') + warning("unrecognized control line\n"); + while (*cp) + if (*cp++ == '\n') { + if (cp == limit + 1) + nextline(); + else + break; + } +} + diff --git a/src/tools/lcc/src/lex.c b/src/tools/lcc/src/lex.c new file mode 100644 index 0000000..ec2f1ec --- /dev/null +++ b/src/tools/lcc/src/lex.c @@ -0,0 +1,923 @@ +#include "c.h" +#include <float.h> +#include <errno.h> + + +#define MAXTOKEN 32 + +enum { BLANK=01, NEWLINE=02, LETTER=04, + DIGIT=010, HEX=020, OTHER=040 }; + +static unsigned char map[256] = { /* 000 nul */ 0, + /* 001 soh */ 0, + /* 002 stx */ 0, + /* 003 etx */ 0, + /* 004 eot */ 0, + /* 005 enq */ 0, + /* 006 ack */ 0, + /* 007 bel */ 0, + /* 010 bs */ 0, + /* 011 ht */ BLANK, + /* 012 nl */ NEWLINE, + /* 013 vt */ BLANK, + /* 014 ff */ BLANK, + /* 015 cr */ 0, + /* 016 so */ 0, + /* 017 si */ 0, + /* 020 dle */ 0, + /* 021 dc1 */ 0, + /* 022 dc2 */ 0, + /* 023 dc3 */ 0, + /* 024 dc4 */ 0, + /* 025 nak */ 0, + /* 026 syn */ 0, + /* 027 etb */ 0, + /* 030 can */ 0, + /* 031 em */ 0, + /* 032 sub */ 0, + /* 033 esc */ 0, + /* 034 fs */ 0, + /* 035 gs */ 0, + /* 036 rs */ 0, + /* 037 us */ 0, + /* 040 sp */ BLANK, + /* 041 ! */ OTHER, + /* 042 " */ OTHER, + /* 043 # */ OTHER, + /* 044 $ */ 0, + /* 045 % */ OTHER, + /* 046 & */ OTHER, + /* 047 ' */ OTHER, + /* 050 ( */ OTHER, + /* 051 ) */ OTHER, + /* 052 * */ OTHER, + /* 053 + */ OTHER, + /* 054 , */ OTHER, + /* 055 - */ OTHER, + /* 056 . */ OTHER, + /* 057 / */ OTHER, + /* 060 0 */ DIGIT, + /* 061 1 */ DIGIT, + /* 062 2 */ DIGIT, + /* 063 3 */ DIGIT, + /* 064 4 */ DIGIT, + /* 065 5 */ DIGIT, + /* 066 6 */ DIGIT, + /* 067 7 */ DIGIT, + /* 070 8 */ DIGIT, + /* 071 9 */ DIGIT, + /* 072 : */ OTHER, + /* 073 ; */ OTHER, + /* 074 < */ OTHER, + /* 075 = */ OTHER, + /* 076 > */ OTHER, + /* 077 ? */ OTHER, + /* 100 @ */ 0, + /* 101 A */ LETTER|HEX, + /* 102 B */ LETTER|HEX, + /* 103 C */ LETTER|HEX, + /* 104 D */ LETTER|HEX, + /* 105 E */ LETTER|HEX, + /* 106 F */ LETTER|HEX, + /* 107 G */ LETTER, + /* 110 H */ LETTER, + /* 111 I */ LETTER, + /* 112 J */ LETTER, + /* 113 K */ LETTER, + /* 114 L */ LETTER, + /* 115 M */ LETTER, + /* 116 N */ LETTER, + /* 117 O */ LETTER, + /* 120 P */ LETTER, + /* 121 Q */ LETTER, + /* 122 R */ LETTER, + /* 123 S */ LETTER, + /* 124 T */ LETTER, + /* 125 U */ LETTER, + /* 126 V */ LETTER, + /* 127 W */ LETTER, + /* 130 X */ LETTER, + /* 131 Y */ LETTER, + /* 132 Z */ LETTER, + /* 133 [ */ OTHER, + /* 134 \ */ OTHER, + /* 135 ] */ OTHER, + /* 136 ^ */ OTHER, + /* 137 _ */ LETTER, + /* 140 ` */ 0, + /* 141 a */ LETTER|HEX, + /* 142 b */ LETTER|HEX, + /* 143 c */ LETTER|HEX, + /* 144 d */ LETTER|HEX, + /* 145 e */ LETTER|HEX, + /* 146 f */ LETTER|HEX, + /* 147 g */ LETTER, + /* 150 h */ LETTER, + /* 151 i */ LETTER, + /* 152 j */ LETTER, + /* 153 k */ LETTER, + /* 154 l */ LETTER, + /* 155 m */ LETTER, + /* 156 n */ LETTER, + /* 157 o */ LETTER, + /* 160 p */ LETTER, + /* 161 q */ LETTER, + /* 162 r */ LETTER, + /* 163 s */ LETTER, + /* 164 t */ LETTER, + /* 165 u */ LETTER, + /* 166 v */ LETTER, + /* 167 w */ LETTER, + /* 170 x */ LETTER, + /* 171 y */ LETTER, + /* 172 z */ LETTER, + /* 173 { */ OTHER, + /* 174 | */ OTHER, + /* 175 } */ OTHER, + /* 176 ~ */ OTHER, }; +static struct symbol tval; +static char cbuf[BUFSIZE+1]; +static unsigned int wcbuf[BUFSIZE+1]; + +Coordinate src; /* current source coordinate */ +int t; +char *token; /* current token */ +Symbol tsym; /* symbol table entry for current token */ + +static void *cput(int c, void *cl); +static void *wcput(int c, void *cl); +static void *scon(int q, void *put(int c, void *cl), void *cl); +static int backslash(int q); +static Symbol fcon(void); +static Symbol icon(unsigned long, int, int); +static void ppnumber(char *); + +int gettok(void) { + for (;;) { + register unsigned char *rcp = cp; + while (map[*rcp]&BLANK) + rcp++; + if (limit - rcp < MAXTOKEN) { + cp = rcp; + fillbuf(); + rcp = cp; + } + src.file = file; + src.x = (char *)rcp - line; + src.y = lineno; + cp = rcp + 1; + switch (*rcp++) { + case '/': if (*rcp == '*') { + int c = 0; + for (rcp++; *rcp != '/' || c != '*'; ) + if (map[*rcp]&NEWLINE) { + if (rcp < limit) + c = *rcp; + cp = rcp + 1; + nextline(); + rcp = cp; + if (rcp == limit) + break; + } else + c = *rcp++; + if (rcp < limit) + rcp++; + else + error("unclosed comment\n"); + cp = rcp; + continue; + } + return '/'; + case '<': + if (*rcp == '=') return cp++, LEQ; + if (*rcp == '<') return cp++, LSHIFT; + return '<'; + case '>': + if (*rcp == '=') return cp++, GEQ; + if (*rcp == '>') return cp++, RSHIFT; + return '>'; + case '-': + if (*rcp == '>') return cp++, DEREF; + if (*rcp == '-') return cp++, DECR; + return '-'; + case '=': return *rcp == '=' ? cp++, EQL : '='; + case '!': return *rcp == '=' ? cp++, NEQ : '!'; + case '|': return *rcp == '|' ? cp++, OROR : '|'; + case '&': return *rcp == '&' ? cp++, ANDAND : '&'; + case '+': return *rcp == '+' ? cp++, INCR : '+'; + case ';': case ',': case ':': + case '*': case '~': case '%': case '^': case '?': + case '[': case ']': case '{': case '}': case '(': case ')': + return rcp[-1]; + case '\n': case '\v': case '\r': case '\f': + nextline(); + if (cp == limit) { + tsym = NULL; + return EOI; + } + continue; + + case 'i': + if (rcp[0] == 'f' + && !(map[rcp[1]]&(DIGIT|LETTER))) { + cp = rcp + 1; + return IF; + } + if (rcp[0] == 'n' + && rcp[1] == 't' + && !(map[rcp[2]]&(DIGIT|LETTER))) { + cp = rcp + 2; + tsym = inttype->u.sym; + return INT; + } + goto id; + case 'h': case 'j': case 'k': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + id: + if (limit - rcp < MAXLINE) { + cp = rcp - 1; + fillbuf(); + rcp = ++cp; + } + assert(cp == rcp); + token = (char *)rcp - 1; + while (map[*rcp]&(DIGIT|LETTER)) + rcp++; + token = stringn(token, (char *)rcp - token); + tsym = lookup(token, identifiers); + cp = rcp; + return ID; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + unsigned long n = 0; + if (limit - rcp < MAXLINE) { + cp = rcp - 1; + fillbuf(); + rcp = ++cp; + } + assert(cp == rcp); + token = (char *)rcp - 1; + if (*token == '0' && (*rcp == 'x' || *rcp == 'X')) { + int d, overflow = 0; + while (*++rcp) { + if (map[*rcp]&DIGIT) + d = *rcp - '0'; + else if (*rcp >= 'a' && *rcp <= 'f') + d = *rcp - 'a' + 10; + else if (*rcp >= 'A' && *rcp <= 'F') + d = *rcp - 'A' + 10; + else + break; + if (n&~(~0UL >> 4)) + overflow = 1; + else + n = (n<<4) + d; + } + if ((char *)rcp - token <= 2) + error("invalid hexadecimal constant `%S'\n", token, (char *)rcp-token); + cp = rcp; + tsym = icon(n, overflow, 16); + } else if (*token == '0') { + int err = 0, overflow = 0; + for ( ; map[*rcp]&DIGIT; rcp++) { + if (*rcp == '8' || *rcp == '9') + err = 1; + if (n&~(~0UL >> 3)) + overflow = 1; + else + n = (n<<3) + (*rcp - '0'); + } + if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') { + cp = rcp; + tsym = fcon(); + return FCON; + } + cp = rcp; + tsym = icon(n, overflow, 8); + if (err) + error("invalid octal constant `%S'\n", token, (char*)cp-token); + } else { + int overflow = 0; + for (n = *token - '0'; map[*rcp]&DIGIT; ) { + int d = *rcp++ - '0'; + if (n > (ULONG_MAX - d)/10) + overflow = 1; + else + n = 10*n + d; + } + if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') { + cp = rcp; + tsym = fcon(); + return FCON; + } + cp = rcp; + tsym = icon(n, overflow, 10); + } + return ICON; + } + case '.': + if (rcp[0] == '.' && rcp[1] == '.') { + cp += 2; + return ELLIPSIS; + } + if ((map[*rcp]&DIGIT) == 0) + return '.'; + if (limit - rcp < MAXLINE) { + cp = rcp - 1; + fillbuf(); + rcp = ++cp; + } + assert(cp == rcp); + cp = rcp - 1; + token = (char *)cp; + tsym = fcon(); + return FCON; + case 'L': + if (*rcp == '\'') { + unsigned int *s = scon(*cp, wcput, wcbuf); + if (s - wcbuf > 2) + warning("excess characters in wide-character literal ignored\n"); + tval.type = widechar; + tval.u.c.v.u = wcbuf[0]; + tsym = &tval; + return ICON; + } else if (*rcp == '"') { + unsigned int *s = scon(*cp, wcput, wcbuf); + tval.type = array(widechar, s - wcbuf, 0); + tval.u.c.v.p = wcbuf; + tsym = &tval; + return SCON; + } else + goto id; + case '\'': { + char *s = scon(*--cp, cput, cbuf); + if (s - cbuf > 2) + warning("excess characters in multibyte character literal ignored\n"); + tval.type = inttype; + if (chartype->op == INT) + tval.u.c.v.i = extend(cbuf[0], chartype); + else + tval.u.c.v.i = cbuf[0]&0xFF; + tsym = &tval; + return ICON; + } + case '"': { + char *s = scon(*--cp, cput, cbuf); + tval.type = array(chartype, s - cbuf, 0); + tval.u.c.v.p = cbuf; + tsym = &tval; + return SCON; + } + case 'a': + if (rcp[0] == 'u' + && rcp[1] == 't' + && rcp[2] == 'o' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return AUTO; + } + goto id; + case 'b': + if (rcp[0] == 'r' + && rcp[1] == 'e' + && rcp[2] == 'a' + && rcp[3] == 'k' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + return BREAK; + } + goto id; + case 'c': + if (rcp[0] == 'a' + && rcp[1] == 's' + && rcp[2] == 'e' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return CASE; + } + if (rcp[0] == 'h' + && rcp[1] == 'a' + && rcp[2] == 'r' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + tsym = chartype->u.sym; + return CHAR; + } + if (rcp[0] == 'o' + && rcp[1] == 'n' + && rcp[2] == 's' + && rcp[3] == 't' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + return CONST; + } + if (rcp[0] == 'o' + && rcp[1] == 'n' + && rcp[2] == 't' + && rcp[3] == 'i' + && rcp[4] == 'n' + && rcp[5] == 'u' + && rcp[6] == 'e' + && !(map[rcp[7]]&(DIGIT|LETTER))) { + cp = rcp + 7; + return CONTINUE; + } + goto id; + case 'd': + if (rcp[0] == 'e' + && rcp[1] == 'f' + && rcp[2] == 'a' + && rcp[3] == 'u' + && rcp[4] == 'l' + && rcp[5] == 't' + && !(map[rcp[6]]&(DIGIT|LETTER))) { + cp = rcp + 6; + return DEFAULT; + } + if (rcp[0] == 'o' + && rcp[1] == 'u' + && rcp[2] == 'b' + && rcp[3] == 'l' + && rcp[4] == 'e' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + tsym = doubletype->u.sym; + return DOUBLE; + } + if (rcp[0] == 'o' + && !(map[rcp[1]]&(DIGIT|LETTER))) { + cp = rcp + 1; + return DO; + } + goto id; + case 'e': + if (rcp[0] == 'l' + && rcp[1] == 's' + && rcp[2] == 'e' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return ELSE; + } + if (rcp[0] == 'n' + && rcp[1] == 'u' + && rcp[2] == 'm' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return ENUM; + } + if (rcp[0] == 'x' + && rcp[1] == 't' + && rcp[2] == 'e' + && rcp[3] == 'r' + && rcp[4] == 'n' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return EXTERN; + } + goto id; + case 'f': + if (rcp[0] == 'l' + && rcp[1] == 'o' + && rcp[2] == 'a' + && rcp[3] == 't' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + tsym = floattype->u.sym; + return FLOAT; + } + if (rcp[0] == 'o' + && rcp[1] == 'r' + && !(map[rcp[2]]&(DIGIT|LETTER))) { + cp = rcp + 2; + return FOR; + } + goto id; + case 'g': + if (rcp[0] == 'o' + && rcp[1] == 't' + && rcp[2] == 'o' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return GOTO; + } + goto id; + case 'l': + if (rcp[0] == 'o' + && rcp[1] == 'n' + && rcp[2] == 'g' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + return LONG; + } + goto id; + case 'r': + if (rcp[0] == 'e' + && rcp[1] == 'g' + && rcp[2] == 'i' + && rcp[3] == 's' + && rcp[4] == 't' + && rcp[5] == 'e' + && rcp[6] == 'r' + && !(map[rcp[7]]&(DIGIT|LETTER))) { + cp = rcp + 7; + return REGISTER; + } + if (rcp[0] == 'e' + && rcp[1] == 't' + && rcp[2] == 'u' + && rcp[3] == 'r' + && rcp[4] == 'n' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return RETURN; + } + goto id; + case 's': + if (rcp[0] == 'h' + && rcp[1] == 'o' + && rcp[2] == 'r' + && rcp[3] == 't' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + return SHORT; + } + if (rcp[0] == 'i' + && rcp[1] == 'g' + && rcp[2] == 'n' + && rcp[3] == 'e' + && rcp[4] == 'd' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return SIGNED; + } + if (rcp[0] == 'i' + && rcp[1] == 'z' + && rcp[2] == 'e' + && rcp[3] == 'o' + && rcp[4] == 'f' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return SIZEOF; + } + if (rcp[0] == 't' + && rcp[1] == 'a' + && rcp[2] == 't' + && rcp[3] == 'i' + && rcp[4] == 'c' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return STATIC; + } + if (rcp[0] == 't' + && rcp[1] == 'r' + && rcp[2] == 'u' + && rcp[3] == 'c' + && rcp[4] == 't' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return STRUCT; + } + if (rcp[0] == 'w' + && rcp[1] == 'i' + && rcp[2] == 't' + && rcp[3] == 'c' + && rcp[4] == 'h' + && !(map[rcp[5]]&(DIGIT|LETTER))) { + cp = rcp + 5; + return SWITCH; + } + goto id; + case 't': + if (rcp[0] == 'y' + && rcp[1] == 'p' + && rcp[2] == 'e' + && rcp[3] == 'd' + && rcp[4] == 'e' + && rcp[5] == 'f' + && !(map[rcp[6]]&(DIGIT|LETTER))) { + cp = rcp + 6; + return TYPEDEF; + } + goto id; + case 'u': + if (rcp[0] == 'n' + && rcp[1] == 'i' + && rcp[2] == 'o' + && rcp[3] == 'n' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + return UNION; + } + if (rcp[0] == 'n' + && rcp[1] == 's' + && rcp[2] == 'i' + && rcp[3] == 'g' + && rcp[4] == 'n' + && rcp[5] == 'e' + && rcp[6] == 'd' + && !(map[rcp[7]]&(DIGIT|LETTER))) { + cp = rcp + 7; + return UNSIGNED; + } + goto id; + case 'v': + if (rcp[0] == 'o' + && rcp[1] == 'i' + && rcp[2] == 'd' + && !(map[rcp[3]]&(DIGIT|LETTER))) { + cp = rcp + 3; + tsym = voidtype->u.sym; + return VOID; + } + if (rcp[0] == 'o' + && rcp[1] == 'l' + && rcp[2] == 'a' + && rcp[3] == 't' + && rcp[4] == 'i' + && rcp[5] == 'l' + && rcp[6] == 'e' + && !(map[rcp[7]]&(DIGIT|LETTER))) { + cp = rcp + 7; + return VOLATILE; + } + goto id; + case 'w': + if (rcp[0] == 'h' + && rcp[1] == 'i' + && rcp[2] == 'l' + && rcp[3] == 'e' + && !(map[rcp[4]]&(DIGIT|LETTER))) { + cp = rcp + 4; + return WHILE; + } + goto id; + case '_': + if (rcp[0] == '_' + && rcp[1] == 't' + && rcp[2] == 'y' + && rcp[3] == 'p' + && rcp[4] == 'e' + && rcp[5] == 'c' + && rcp[6] == 'o' + && rcp[7] == 'd' + && rcp[8] == 'e' + && !(map[rcp[9]]&(DIGIT|LETTER))) { + cp = rcp + 9; + return TYPECODE; + } + if (rcp[0] == '_' + && rcp[1] == 'f' + && rcp[2] == 'i' + && rcp[3] == 'r' + && rcp[4] == 's' + && rcp[5] == 't' + && rcp[6] == 'a' + && rcp[7] == 'r' + && rcp[8] == 'g' + && !(map[rcp[9]]&(DIGIT|LETTER))) { + cp = rcp + 9; + return FIRSTARG; + } + goto id; + default: + if ((map[cp[-1]]&BLANK) == 0) { + if (cp[-1] < ' ' || cp[-1] >= 0177) + error("illegal character `\\0%o'\n", cp[-1]); + else + error("illegal character `%c'\n", cp[-1]); + } + } + } +} +static Symbol icon(unsigned long n, int overflow, int base) { + if (((*cp=='u'||*cp=='U') && (cp[1]=='l'||cp[1]=='L')) + || ((*cp=='l'||*cp=='L') && (cp[1]=='u'||cp[1]=='U'))) { + tval.type = unsignedlong; + cp += 2; + } else if (*cp == 'u' || *cp == 'U') { + if (overflow || n > unsignedtype->u.sym->u.limits.max.i) + tval.type = unsignedlong; + else + tval.type = unsignedtype; + cp += 1; + } else if (*cp == 'l' || *cp == 'L') { + if (overflow || n > longtype->u.sym->u.limits.max.i) + tval.type = unsignedlong; + else + tval.type = longtype; + cp += 1; + } else if (overflow || n > longtype->u.sym->u.limits.max.i) + tval.type = unsignedlong; + else if (n > inttype->u.sym->u.limits.max.i) + tval.type = longtype; + else if (base != 10 && n > inttype->u.sym->u.limits.max.i) + tval.type = unsignedtype; + else + tval.type = inttype; + switch (tval.type->op) { + case INT: + if (overflow || n > tval.type->u.sym->u.limits.max.i) { + warning("overflow in constant `%S'\n", token, + (char*)cp - token); + tval.u.c.v.i = tval.type->u.sym->u.limits.max.i; + } else + tval.u.c.v.i = n; + break; + case UNSIGNED: + if (overflow || n > tval.type->u.sym->u.limits.max.u) { + warning("overflow in constant `%S'\n", token, + (char*)cp - token); + tval.u.c.v.u = tval.type->u.sym->u.limits.max.u; + } else + tval.u.c.v.u = n; + break; + default: assert(0); + } + ppnumber("integer"); + return &tval; +} +static void ppnumber(char *which) { + unsigned char *rcp = cp--; + + for ( ; (map[*cp]&(DIGIT|LETTER)) || *cp == '.'; cp++) + if ((cp[0] == 'E' || cp[0] == 'e') + && (cp[1] == '-' || cp[1] == '+')) + cp++; + if (cp > rcp) + error("`%S' is a preprocessing number but an invalid %s constant\n", token, + + (char*)cp-token, which); +} +static Symbol fcon(void) { + if (*cp == '.') + do + cp++; + while (map[*cp]&DIGIT); + if (*cp == 'e' || *cp == 'E') { + if (*++cp == '-' || *cp == '+') + cp++; + if (map[*cp]&DIGIT) + do + cp++; + while (map[*cp]&DIGIT); + else + error("invalid floating constant `%S'\n", token, + (char*)cp - token); + } + + errno = 0; + tval.u.c.v.d = strtod(token, NULL); + if (errno == ERANGE) + warning("overflow in floating constant `%S'\n", token, + (char*)cp - token); + if (*cp == 'f' || *cp == 'F') { + ++cp; + if (tval.u.c.v.d > floattype->u.sym->u.limits.max.d) + warning("overflow in floating constant `%S'\n", token, + (char*)cp - token); + tval.type = floattype; + } else if (*cp == 'l' || *cp == 'L') { + cp++; + tval.type = longdouble; + } else { + if (tval.u.c.v.d > doubletype->u.sym->u.limits.max.d) + warning("overflow in floating constant `%S'\n", token, + (char*)cp - token); + tval.type = doubletype; + } + ppnumber("floating"); + return &tval; +} + +static void *cput(int c, void *cl) { + char *s = cl; + + if (c < 0 || c > 255) + warning("overflow in escape sequence with resulting value `%d'\n", c); + *s++ = c; + return s; +} + +static void *wcput(int c, void *cl) { + unsigned int *s = cl; + + *s++ = c; + return s; +} + +static void *scon(int q, void *put(int c, void *cl), void *cl) { + int n = 0, nbad = 0; + + do { + cp++; + while (*cp != q) { + int c; + if (map[*cp]&NEWLINE) { + if (cp < limit) + break; + cp++; + nextline(); + if (cp == limit) + break; + continue; + } + c = *cp++; + if (c == '\\') { + if (map[*cp]&NEWLINE) { + if (cp < limit) + break; + cp++; + nextline(); + } + if (limit - cp < MAXTOKEN) + fillbuf(); + c = backslash(q); + } else if (c < 0 || c > 255 || map[c] == 0) + nbad++; + if (n++ < BUFSIZE) + cl = put(c, cl); + } + if (*cp == q) + cp++; + else + error("missing %c\n", q); + } while (q == '"' && getchr() == '"'); + cl = put(0, cl); + if (n >= BUFSIZE) + error("%s literal too long\n", q == '"' ? "string" : "character"); + if (Aflag >= 2 && q == '"' && n > 509) + warning("more than 509 characters in a string literal\n"); + if (Aflag >= 2 && nbad > 0) + warning("%s literal contains non-portable characters\n", + q == '"' ? "string" : "character"); + return cl; +} +int getchr(void) { + for (;;) { + while (map[*cp]&BLANK) + cp++; + if (!(map[*cp]&NEWLINE)) + return *cp; + cp++; + nextline(); + if (cp == limit) + return EOI; + } +} +static int backslash(int q) { + unsigned int c; + + switch (*cp++) { + case 'a': return 7; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '\'': case '"': case '\\': case '\?': break; + case 'x': { + int overflow = 0; + if ((map[*cp]&(DIGIT|HEX)) == 0) { + if (*cp < ' ' || *cp == 0177) + error("ill-formed hexadecimal escape sequence\n"); + else + error("ill-formed hexadecimal escape sequence `\\x%c'\n", *cp); + if (*cp != q) + cp++; + return 0; + } + for (c = 0; map[*cp]&(DIGIT|HEX); cp++) { + if (c >> (8*widechar->size - 4)) + overflow = 1; + if (map[*cp]&DIGIT) + c = (c<<4) + *cp - '0'; + else + c = (c<<4) + (*cp&~040) - 'A' + 10; + } + if (overflow) + warning("overflow in hexadecimal escape sequence\n"); + return c&ones(8*widechar->size); + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = *(cp-1) - '0'; + if (*cp >= '0' && *cp <= '7') { + c = (c<<3) + *cp++ - '0'; + if (*cp >= '0' && *cp <= '7') + c = (c<<3) + *cp++ - '0'; + } + return c; + default: + if (cp[-1] < ' ' || cp[-1] >= 0177) + warning("unrecognized character escape sequence\n"); + else + warning("unrecognized character escape sequence `\\%c'\n", cp[-1]); + } + return cp[-1]; +} diff --git a/src/tools/lcc/src/list.c b/src/tools/lcc/src/list.c new file mode 100644 index 0000000..29e660a --- /dev/null +++ b/src/tools/lcc/src/list.c @@ -0,0 +1,56 @@ +#include "c.h" + + +static List freenodes; /* free list nodes */ + +/* append - append x to list, return new list */ +List append(void *x, List list) { + List new; + + if ((new = freenodes) != NULL) + freenodes = freenodes->link; + else + NEW(new, PERM); + if (list) { + new->link = list->link; + list->link = new; + } else + new->link = new; + new->x = x; + return new; +} + +/* length - # elements in list */ +int length(List list) { + int n = 0; + + if (list) { + List lp = list; + do + n++; + while ((lp = lp->link) != list); + } + return n; +} + +/* ltov - convert list to an NULL-terminated vector allocated in arena */ +void *ltov(List *list, unsigned arena) { + int i = 0; + void **array = newarray(length(*list) + 1, sizeof array[0], arena); + + if (*list) { + List lp = *list; + do { + lp = lp->link; + array[i++] = lp->x; + } while (lp != *list); +#ifndef PURIFY + lp = (*list)->link; + (*list)->link = freenodes; + freenodes = lp; +#endif + } + *list = NULL; + array[i] = NULL; + return array; +} diff --git a/src/tools/lcc/src/main.c b/src/tools/lcc/src/main.c new file mode 100644 index 0000000..63b85f2 --- /dev/null +++ b/src/tools/lcc/src/main.c @@ -0,0 +1,225 @@ +#include "c.h" + +static char rcsid[] = "main.c - faked rcsid"; + +static void typestab(Symbol, void *); + +static void stabline(Coordinate *); +static void stabend(Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *); +Interface *IR = NULL; + +int Aflag; /* >= 0 if -A specified */ +int Pflag; /* != 0 if -P specified */ +int glevel; /* == [0-9] if -g[0-9] specified */ +int xref; /* != 0 for cross-reference data */ +Symbol YYnull; /* _YYnull symbol if -n or -nvalidate specified */ +Symbol YYcheck; /* _YYcheck symbol if -nvalidate,check specified */ + +static char *comment; +static Interface stabIR; +static char *currentfile; /* current file name */ +static int currentline; /* current line number */ +static FILE *srcfp; /* stream for current file, if non-NULL */ +static int srcpos; /* position of srcfp, if srcfp is non-NULL */ +int main(int argc, char *argv[]) { + int i, j; + for (i = argc - 1; i > 0; i--) + if (strncmp(argv[i], "-target=", 8) == 0) + break; + if (i > 0) { + char *s = strchr(argv[i], '\\'); + if (s != NULL) + *s = '/'; + for (j = 0; bindings[j].name && bindings[j].ir; j++) + if (strcmp(&argv[i][8], bindings[j].name) == 0) { + IR = bindings[j].ir; + break; + } + if (s != NULL) + *s = '\\'; + } + if (!IR) { + fprint(stderr, "%s: unknown target", argv[0]); + if (i > 0) + fprint(stderr, " `%s'", &argv[i][8]); + fprint(stderr, "; must specify one of\n"); + for (i = 0; bindings[i].name; i++) + fprint(stderr, "\t-target=%s\n", bindings[i].name); + exit(EXIT_FAILURE); + } + init(argc, argv); + t = gettok(); + (*IR->progbeg)(argc, argv); + if (glevel && IR->stabinit) + (*IR->stabinit)(firstfile, argc, argv); + program(); + if (events.end) + apply(events.end, NULL, NULL); + memset(&events, 0, sizeof events); + if (glevel || xref) { + Symbol symroot = NULL; + Coordinate src; + foreach(types, GLOBAL, typestab, &symroot); + foreach(identifiers, GLOBAL, typestab, &symroot); + src.file = firstfile; + src.x = 0; + src.y = lineno; + if ((glevel > 2 || xref) && IR->stabend) + (*IR->stabend)(&src, symroot, + ltov(&loci, PERM), + ltov(&symbols, PERM), NULL); + else if (IR->stabend) + (*IR->stabend)(&src, NULL, NULL, NULL, NULL); + } + finalize(); + (*IR->progend)(); + deallocate(PERM); + return errcnt > 0; +} +/* main_init - process program arguments */ +void main_init(int argc, char *argv[]) { + char *infile = NULL, *outfile = NULL; + int i; + static int inited; + + if (inited) + return; + inited = 1; + type_init(argc, argv); + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "-g2") == 0) + glevel = 2; + else if (strncmp(argv[i], "-g", 2) == 0) { /* -gn[,x] */ + char *p = strchr(argv[i], ','); + glevel = atoi(argv[i]+2); + if (p) { + comment = p + 1; + if (glevel == 0) + glevel = 1; + if (stabIR.stabline == NULL) { + stabIR.stabline = IR->stabline; + stabIR.stabend = IR->stabend; + IR->stabline = stabline; + IR->stabend = stabend; + } + } + } else if (strcmp(argv[i], "-x") == 0) + xref++; + else if (strcmp(argv[i], "-A") == 0) { + ++Aflag; + } else if (strcmp(argv[i], "-P") == 0) + Pflag++; + else if (strcmp(argv[i], "-w") == 0) + wflag++; + else if (strcmp(argv[i], "-n") == 0) { + if (!YYnull) { + YYnull = install(string("_YYnull"), &globals, GLOBAL, PERM); + YYnull->type = func(voidptype, NULL, 1); + YYnull->sclass = EXTERN; + (*IR->defsymbol)(YYnull); + } + } else if (strncmp(argv[i], "-n", 2) == 0) { /* -nvalid[,check] */ + char *p = strchr(argv[i], ','); + if (p) { + YYcheck = install(string(p+1), &globals, GLOBAL, PERM); + YYcheck->type = func(voidptype, NULL, 1); + YYcheck->sclass = EXTERN; + (*IR->defsymbol)(YYcheck); + p = stringn(argv[i]+2, p - (argv[i]+2)); + } else + p = string(argv[i]+2); + YYnull = install(p, &globals, GLOBAL, PERM); + YYnull->type = func(voidptype, NULL, 1); + YYnull->sclass = EXTERN; + (*IR->defsymbol)(YYnull); + } else if (strcmp(argv[i], "-v") == 0) + fprint(stderr, "%s %s\n", argv[0], rcsid); + else if (strncmp(argv[i], "-s", 2) == 0) + density = strtod(&argv[i][2], NULL); + else if (strncmp(argv[i], "-errout=", 8) == 0) { + FILE *f = fopen(argv[i]+8, "w"); + if (f == NULL) { + fprint(stderr, "%s: can't write errors to `%s'\n", argv[0], argv[i]+8); + exit(EXIT_FAILURE); + } + fclose(f); + f = freopen(argv[i]+8, "w", stderr); + assert(f); + } else if (strncmp(argv[i], "-e", 2) == 0) { + int x; + if ((x = strtol(&argv[i][2], NULL, 0)) > 0) + errlimit = x; + } else if (strncmp(argv[i], "-little_endian=", 15) == 0) + IR->little_endian = argv[i][15] - '0'; + else if (strncmp(argv[i], "-mulops_calls=", 18) == 0) + IR->mulops_calls = argv[i][18] - '0'; + else if (strncmp(argv[i], "-wants_callb=", 13) == 0) + IR->wants_callb = argv[i][13] - '0'; + else if (strncmp(argv[i], "-wants_argb=", 12) == 0) + IR->wants_argb = argv[i][12] - '0'; + else if (strncmp(argv[i], "-left_to_right=", 15) == 0) + IR->left_to_right = argv[i][15] - '0'; + else if (strncmp(argv[i], "-wants_dag=", 11) == 0) + IR->wants_dag = argv[i][11] - '0'; + else if (*argv[i] != '-' || strcmp(argv[i], "-") == 0) { + if (infile == NULL) + infile = argv[i]; + else if (outfile == NULL) + outfile = argv[i]; + } + + if (infile != NULL && strcmp(infile, "-") != 0 + && freopen(infile, "r", stdin) == NULL) { + fprint(stderr, "%s: can't read `%s'\n", argv[0], infile); + exit(EXIT_FAILURE); + } + if (outfile != NULL && strcmp(outfile, "-") != 0 + && freopen(outfile, "w", stdout) == NULL) { + fprint(stderr, "%s: can't write `%s'\n", argv[0], outfile); + exit(EXIT_FAILURE); + } +} +/* typestab - emit stab entries for p */ +static void typestab(Symbol p, void *cl) { + if (*(Symbol *)cl == 0 && p->sclass && p->sclass != TYPEDEF) + *(Symbol *)cl = p; + if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype) + (*IR->stabtype)(p); +} + +/* stabline - emit source code for source coordinate *cp */ +static void stabline(Coordinate *cp) { + if (cp->file && cp->file != currentfile) { + if (srcfp) + fclose(srcfp); + currentfile = cp->file; + srcfp = fopen(currentfile, "r"); + srcpos = 0; + currentline = 0; + } + if (currentline != cp->y && srcfp) { + char buf[512]; + if (srcpos > cp->y) { + rewind(srcfp); + srcpos = 0; + } + for ( ; srcpos < cp->y; srcpos++) + if (fgets(buf, sizeof buf, srcfp) == NULL) { + fclose(srcfp); + srcfp = NULL; + break; + } + if (srcfp && srcpos == cp->y) + print("%s%s", comment, buf); + } + currentline = cp->y; + if (stabIR.stabline) + (*stabIR.stabline)(cp); +} + +static void stabend(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) { + if (stabIR.stabend) + (*stabIR.stabend)(cp, p, cpp, sp, stab); + if (srcfp) + fclose(srcfp); +} diff --git a/src/tools/lcc/src/null.c b/src/tools/lcc/src/null.c new file mode 100644 index 0000000..b9f551c --- /dev/null +++ b/src/tools/lcc/src/null.c @@ -0,0 +1,74 @@ +#include "c.h" +#define I(f) null_##f + +static Node I(gen)(Node p) { return p; } +static void I(address)(Symbol q, Symbol p, long n) {} +static void I(blockbeg)(Env *e) {} +static void I(blockend)(Env *e) {} +static void I(defaddress)(Symbol p) {} +static void I(defconst)(int suffix, int size, Value v) {} +static void I(defstring)(int len, char *s) {} +static void I(defsymbol)(Symbol p) {} +static void I(emit)(Node p) {} +static void I(export)(Symbol p) {} +static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) {} +static void I(global)(Symbol p) {} +static void I(import)(Symbol p) {} +static void I(local)(Symbol p) {} +static void I(progbeg)(int argc, char *argv[]) {} +static void I(progend)(void) {} +static void I(segment)(int s) {} +static void I(space)(int n) {} +static void I(stabblock)(int brace, int lev, Symbol *p) {} +static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) {} +static void I(stabfend)(Symbol p, int lineno) {} +static void I(stabinit)(char *file, int argc, char *argv[]) {} +static void I(stabline)(Coordinate *cp) {} +static void I(stabsym)(Symbol p) {} +static void I(stabtype)(Symbol p) {} + + +Interface nullIR = { + {1, 1, 0}, /* char */ + {2, 2, 0}, /* short */ + {4, 4, 0}, /* int */ + {8, 8, 1}, /* long */ + {8 ,8, 1}, /* long long */ + {4, 4, 1}, /* float */ + {8, 8, 1}, /* double */ + {16,16,1}, /* long double */ + {4, 4, 0}, /* T* */ + {0, 4, 0}, /* struct */ + 1, /* little_endian */ + 0, /* mulops_calls */ + 0, /* wants_callb */ + 0, /* wants_argb */ + 1, /* left_to_right */ + 0, /* wants_dag */ + 0, /* unsigned_char */ + I(address), + I(blockbeg), + I(blockend), + I(defaddress), + I(defconst), + I(defstring), + I(defsymbol), + I(emit), + I(export), + I(function), + I(gen), + I(global), + I(import), + I(local), + I(progbeg), + I(progend), + I(segment), + I(space), + I(stabblock), + I(stabend), + I(stabfend), + I(stabinit), + I(stabline), + I(stabsym), + I(stabtype) +}; diff --git a/src/tools/lcc/src/output.c b/src/tools/lcc/src/output.c new file mode 100644 index 0000000..a9c93e7 --- /dev/null +++ b/src/tools/lcc/src/output.c @@ -0,0 +1,135 @@ +#include "c.h" + + +static char *outs(const char *str, FILE *f, char *bp) { + if (f) + fputs(str, f); + else + while ((*bp = *str++)) + bp++; + return bp; +} + +static char *outd(long n, FILE *f, char *bp) { + unsigned long m; + char buf[25], *s = buf + sizeof buf; + + *--s = '\0'; + if (n < 0) + m = -n; + else + m = n; + do + *--s = m%10 + '0'; + while ((m /= 10) != 0); + if (n < 0) + *--s = '-'; + return outs(s, f, bp); +} + +static char *outu(unsigned long n, int base, FILE *f, char *bp) { + char buf[25], *s = buf + sizeof buf; + + *--s = '\0'; + do + *--s = "0123456789abcdef"[n%base]; + while ((n /= base) != 0); + return outs(s, f, bp); +} +void print(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprint(stdout, NULL, fmt, ap); + va_end(ap); +} +/* fprint - formatted output to f */ +void fprint(FILE *f, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprint(f, NULL, fmt, ap); + va_end(ap); +} + +/* stringf - formatted output to a saved string */ +char *stringf(const char *fmt, ...) { + char buf[1024]; + va_list ap; + + va_start(ap, fmt); + vfprint(NULL, buf, fmt, ap); + va_end(ap); + return string(buf); +} + +/* vfprint - formatted output to f or string bp */ +void vfprint(FILE *f, char *bp, const char *fmt, va_list ap) { + for (; *fmt; fmt++) + if (*fmt == '%') + switch (*++fmt) { + case 'd': bp = outd(va_arg(ap, int), f, bp); break; + case 'D': bp = outd(va_arg(ap, long), f, bp); break; + case 'U': bp = outu(va_arg(ap, unsigned long), 10, f, bp); break; + case 'u': bp = outu(va_arg(ap, unsigned), 10, f, bp); break; + case 'o': bp = outu(va_arg(ap, unsigned), 8, f, bp); break; + case 'X': bp = outu(va_arg(ap, unsigned long), 16, f, bp); break; + case 'x': bp = outu(va_arg(ap, unsigned), 16, f, bp); break; + case 'f': case 'e': + case 'g': { + static char format[] = "%f"; + char buf[128]; + format[1] = *fmt; + sprintf(buf, format, va_arg(ap, double)); + bp = outs(buf, f, bp); + } +; break; + case 's': bp = outs(va_arg(ap, char *), f, bp); break; + case 'p': { + void *p = va_arg(ap, void *); + if (p) + bp = outs("0x", f, bp); + bp = outu((unsigned long)p, 16, f, bp); + break; + } + case 'c': if (f) fputc(va_arg(ap, int), f); else *bp++ = va_arg(ap, int); break; + case 'S': { char *s = va_arg(ap, char *); + int n = va_arg(ap, int); + if (s) { + for ( ; n-- > 0; s++) + if (f) (void)putc(*s, f); else *bp++ = *s; + } + } break; + case 'k': { int t = va_arg(ap, int); + static char *tokens[] = { +#define xx(a,b,c,d,e,f,g) g, +#define yy(a,b,c,d,e,f,g) g, +#include "token.h" + }; + assert(tokens[t&0177]); + bp = outs(tokens[t&0177], f, bp); + } break; + case 't': { Type ty = va_arg(ap, Type); + assert(f); + outtype(ty ? ty : voidtype, f); + } break; + case 'w': { Coordinate *p = va_arg(ap, Coordinate *); + if (p->file && *p->file) { + bp = outs(p->file, f, bp); + bp = outs(":", f, bp); + } + bp = outd(p->y, f, bp); + } break; + case 'I': { int n = va_arg(ap, int); + while (--n >= 0) + if (f) (void)putc(' ', f); else *bp++ = ' '; + } break; + default: if (f) (void)putc(*fmt, f); else *bp++ = *fmt; break; + } + else if (f) + (void)putc(*fmt, f); + else + *bp++ = *fmt; + if (!f) + *bp = '\0'; +} diff --git a/src/tools/lcc/src/prof.c b/src/tools/lcc/src/prof.c new file mode 100644 index 0000000..02709ed --- /dev/null +++ b/src/tools/lcc/src/prof.c @@ -0,0 +1,228 @@ +#include "c.h" + + +struct callsite { + char *file, *name; + union coordinate { + unsigned int coord; + struct { unsigned int y:16,x:10,index:6; } le; + struct { unsigned int index:6,x:10,y:16; } be; + } u; +}; +struct func { + struct func *link; + struct caller *callers; + char *name; + union coordinate src; +}; +struct map { /* source code map; 200 coordinates/map */ + int size; + union coordinate u[200]; +}; + +int npoints; /* # of execution points if -b specified */ +int ncalled = -1; /* #times prof.out says current function was called */ +static Symbol YYlink; /* symbol for file's struct _bbdata */ +static Symbol YYcounts; /* symbol for _YYcounts if -b specified */ +static List maplist; /* list of struct map *'s */ +static List filelist; /* list of file names */ +static Symbol funclist; /* list of struct func *'s */ +static Symbol afunc; /* current function's struct func */ + +/* bbcall - build tree to set _callsite at call site *cp, emit call site data */ +static void bbcall(Symbol yycounts, Coordinate *cp, Tree *e) { + static Symbol caller; + Value v; + union coordinate u; + Symbol p = genident(STATIC, array(voidptype, 0, 0), GLOBAL); + Tree t = *e; + + defglobal(p, LIT); + defpointer(cp->file ? mkstr(cp->file)->u.c.loc : (Symbol)0); + defpointer(mkstr(cfunc->name)->u.c.loc); + if (IR->little_endian) { + u.le.x = cp->x; + u.le.y = cp->y; + } else { + u.be.x = cp->x; + u.be.y = cp->y; + } + (*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v)); + if (caller == 0) { + caller = mksymbol(EXTERN, "_caller", ptr(voidptype)); + caller->defined = 0; + } + if (generic((*e)->op) != CALL) + t = (*e)->kids[0]; + assert(generic(t->op) == CALL); + t = tree(t->op, t->type, + tree(RIGHT, t->kids[0]->type, + t->kids[0], + tree(RIGHT, t->kids[0]->type, asgn(caller, idtree(p)), t->kids[0])), + t->kids[1]); + if (generic((*e)->op) != CALL) + t = tree((*e)->op, (*e)->type, t, (*e)->kids[1]); + *e = t; +} + +/* bbentry - return tree for _prologue(&afunc, &YYlink)' */ +static void bbentry(Symbol yylink, Symbol f) { + static Symbol prologue; + + afunc = genident(STATIC, array(voidptype, 4, 0), GLOBAL); + if (prologue == 0) { + prologue = mksymbol(EXTERN, "_prologue", ftype(inttype, voidptype)); + prologue->defined = 0; + } + walk(vcall(prologue, voidtype, pointer(idtree(afunc)), pointer(idtree(yylink)), NULL), 0, 0); + +} + +/* bbexit - return tree for _epilogue(&afunc)' */ +static void bbexit(Symbol yylink, Symbol f, Tree e) { + static Symbol epilogue; + + if (epilogue == 0) { + epilogue = mksymbol(EXTERN, "_epilogue", ftype(inttype, voidptype)); + epilogue->defined = 0; + } + walk(vcall(epilogue, voidtype, pointer(idtree(afunc)), NULL), 0, 0); +} + +/* bbfile - add file to list of file names, return its index */ +static int bbfile(char *file) { + if (file) { + List lp; + int i = 1; + if ((lp = filelist) != NULL) + do { + lp = lp->link; + if (((Symbol)lp->x)->u.c.v.p == file) + return i; + i++; + } while (lp != filelist); + filelist = append(mkstr(file), filelist); + return i; + } + return 0; +} + +/* bbfunc - emit function name and src coordinates */ +static void bbfunc(Symbol yylink, Symbol f) { + Value v; + union coordinate u; + + defglobal(afunc, DATA); + defpointer(funclist); + defpointer(NULL); + defpointer(mkstr(f->name)->u.c.loc); + if (IR->little_endian) { + u.le.x = f->u.f.pt.x; + u.le.y = f->u.f.pt.y; + u.le.index = bbfile(f->u.f.pt.file); + } else { + u.be.x = f->u.f.pt.x; + u.be.y = f->u.f.pt.y; + u.be.index = bbfile(f->u.f.pt.file); + } + (*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v)); + funclist = afunc; +} + +/* bbincr - build tree to increment execution point at *cp */ +static void bbincr(Symbol yycounts, Coordinate *cp, Tree *e) { + struct map *mp = maplist->x; + Tree t; + + /* append *cp to source map */ + if (mp->size >= NELEMS(mp->u)) { + NEW(mp, PERM); + mp->size = 0; + maplist = append(mp, maplist); + } + if (IR->little_endian) { + mp->u[mp->size].le.x = cp->x; + mp->u[mp->size].le.y = cp->y; + mp->u[mp->size++].le.index = bbfile(cp->file); + } else { + mp->u[mp->size].be.x = cp->x; + mp->u[mp->size].be.y = cp->y; + mp->u[mp->size++].be.index = bbfile(cp->file); + } + t = incr('+', rvalue((*optree['+'])(ADD, pointer(idtree(yycounts)), + consttree(npoints++, inttype))), consttree(1, inttype)); + if (*e) + *e = tree(RIGHT, (*e)->type, t, *e); + else + *e = t; +} + +/* bbvars - emit definition for basic block counting data */ +static void bbvars(Symbol yylink) { + int i, j, n = npoints; + Value v; + struct map **mp; + Symbol coords, files, *p; + + if (!YYcounts && !yylink) + return; + if (YYcounts) { + if (n <= 0) + n = 1; + YYcounts->type = array(unsignedtype, n, 0); + defglobal(YYcounts, BSS); + } + files = genident(STATIC, array(charptype, 1, 0), GLOBAL); + defglobal(files, LIT); + for (p = ltov(&filelist, PERM); *p; p++) + defpointer((*p)->u.c.loc); + defpointer(NULL); + coords = genident(STATIC, array(unsignedtype, n, 0), GLOBAL); + defglobal(coords, LIT); + for (i = n, mp = ltov(&maplist, PERM); *mp; i -= (*mp)->size, mp++) + for (j = 0; j < (*mp)->size; j++) + (*IR->defconst)(U, unsignedtype->size, (v.u = (*mp)->u[j].coord, v)); + if (i > 0) + (*IR->space)(i*coords->type->type->size); + defpointer(NULL); + defglobal(yylink, DATA); + defpointer(NULL); + (*IR->defconst)(U, unsignedtype->size, (v.u = n, v)); + defpointer(YYcounts); + defpointer(coords); + defpointer(files); + defpointer(funclist); +} + +/* profInit - initialize basic block profiling options */ +void prof_init(int argc, char *argv[]) { + int i; + static int inited; + + if (inited) + return; + inited = 1; + type_init(argc, argv); + if (IR) { + for (i = 1; i < argc; i++) + if (strncmp(argv[i], "-a", 2) == 0) { + if (ncalled == -1 + && process(argv[i][2] ? &argv[i][2] : "prof.out") > 0) + ncalled = 0; + } else if ((strcmp(argv[i], "-b") == 0 + || strcmp(argv[i], "-C") == 0) && YYlink == 0) { + YYlink = genident(STATIC, array(unsignedtype, 0, 0), GLOBAL); + attach((Apply)bbentry, YYlink, &events.entry); + attach((Apply)bbexit, YYlink, &events.returns); + attach((Apply)bbfunc, YYlink, &events.exit); + attach((Apply)bbvars, YYlink, &events.end); + if (strcmp(argv[i], "-b") == 0) { + YYcounts = genident(STATIC, array(unsignedtype, 0, 0), GLOBAL); + maplist = append(allocate(sizeof (struct map), PERM), maplist); + ((struct map *)maplist->x)->size = 0; + attach((Apply)bbcall, YYcounts, &events.calls); + attach((Apply)bbincr, YYcounts, &events.points); + } + } + } +} diff --git a/src/tools/lcc/src/profio.c b/src/tools/lcc/src/profio.c new file mode 100644 index 0000000..37fc25b --- /dev/null +++ b/src/tools/lcc/src/profio.c @@ -0,0 +1,276 @@ +/* C compiler: prof.out input + +prof.out format: +#files + name + ... (#files-1 times) +#functions + name file# x y count caller file x y + ... (#functions-1 times) +#points + file# x y count + ... (#points-1 times) +*/ +#include "c.h" + + +struct count { /* count data: */ + int x, y; /* source coordinate */ + int count; /* associated execution count */ +}; + +#define MAXTOKEN 64 + +struct file { /* per-file prof.out data: */ + struct file *link; /* link to next file */ + char *name; /* file name */ + int size; /* size of counts[] */ + int count; /* counts[0..count-1] hold valid data */ + struct count *counts; /* count data */ + struct func { /* function data: */ + struct func *link; /* link to next function */ + char *name; /* function name */ + struct count count; /* total number of calls */ + struct caller { /* caller data: */ + struct caller *link; /* link to next caller */ + char *name; /* caller's name */ + char *file; /* call site: file, x, y */ + int x, y; + int count; /* number of calls from this site */ + } *callers; + } *funcs; /* list of functions */ +} *filelist; +FILE *fp; + +/* acaller - add caller and site (file,x,y) to callee's callers list */ +static void acaller(char *caller, char *file, int x, int y, int count, struct func *callee) { + struct caller *q; + + assert(callee); + for (q = callee->callers; q && (caller != q->name + || file != q->file || x != q->x || y != q->y); q = q->link) + ; + if (!q) { + struct caller **r; + NEW(q, PERM); + q->name = caller; + q->file = file; + q->x = x; + q->y = y; + q->count = 0; + for (r = &callee->callers; *r && (strcmp(q->name, (*r)->name) > 0 + || strcmp(q->file, (*r)->file) > 0 || q->y > (*r)->y || q->y > (*r)->y); r = &(*r)->link) + ; + q->link = *r; + *r = q; + } + q->count += count; +} + +/* compare - return <0, 0, >0 if a<b, a==b, a>b, resp. */ +static int compare(struct count *a, struct count *b) { + if (a->y == b->y) + return a->x - b->x; + return a->y - b->y; +} + +/* findfile - return file name's file list entry, or 0 */ +static struct file *findfile(char *name) { + struct file *p; + + for (p = filelist; p; p = p->link) + if (p->name == name) + return p; + return 0; +} + +/* afunction - add function name and its data to file's function list */ +static struct func *afunction(char *name, char *file, int x, int y, int count) { + struct file *p = findfile(file); + struct func *q; + + assert(p); + for (q = p->funcs; q && name != q->name; q = q->link) + ; + if (!q) { + struct func **r; + NEW(q, PERM); + q->name = name; + q->count.x = x; + q->count.y = y; + q->count.count = 0; + q->callers = 0; + for (r = &p->funcs; *r && compare(&q->count, &(*r)->count) > 0; r = &(*r)->link) + ; + q->link = *r; + *r = q; + } + q->count.count += count; + return q; +} + +/* apoint - append execution point i to file's data */ +static void apoint(int i, char *file, int x, int y, int count) { + struct file *p = findfile(file); + + assert(p); + if (i >= p->size) { + int j; + if (p->size == 0) { + p->size = i >= 200 ? 2*i : 200; + p->counts = newarray(p->size, sizeof *p->counts, PERM); + } else { + struct count *new; + p->size = 2*i; + new = newarray(p->size, sizeof *new, PERM); + for (j = 0; j < p->count; j++) + new[j] = p->counts[j]; + p->counts = new; + } + for (j = p->count; j < p->size; j++) { + static struct count z; + p->counts[j] = z; + } + } + p->counts[i].x = x; + p->counts[i].y = y; + p->counts[i].count += count; + if (i >= p->count) + p->count = i + 1; +} + +/* findcount - return count associated with (file,x,y) or -1 */ +int findcount(char *file, int x, int y) { + static struct file *cursor; + + if (cursor == 0 || cursor->name != file) + cursor = findfile(file); + if (cursor) { + int l, u; + struct count *c = cursor->counts; + for (l = 0, u = cursor->count - 1; l <= u; ) { + int k = (l + u)/2; + if (c[k].y > y || (c[k].y == y && c[k].x > x)) + u = k - 1; + else if (c[k].y < y || (c[k].y == y && c[k].x < x)) + l = k + 1; + else + return c[k].count; + } + } + return -1; +} + +/* findfunc - return count associated with function name in file or -1 */ +int findfunc(char *name, char *file) { + static struct file *cursor; + + if (cursor == 0 || cursor->name != file) + cursor = findfile(file); + if (cursor) { + struct func *p; + for (p = cursor->funcs; p; p = p->link) + if (p->name == name) + return p->count.count; + } + return -1; +} + +/* getd - read a nonnegative number */ +static int getd(void) { + int c, n = 0; + + while ((c = getc(fp)) != EOF && (c == ' ' || c == '\n' || c == '\t')) + ; + if (c >= '0' && c <= '9') { + do + n = 10*n + (c - '0'); + while ((c = getc(fp)) >= '0' && c <= '9'); + return n; + } + return -1; +} + +/* getstr - read a string */ +static char *getstr(void) { + int c; + char buf[MAXTOKEN], *s = buf; + + while ((c = getc(fp)) != EOF && c != ' ' && c != '\n' && c != '\t') + if (s - buf < (int)sizeof buf - 2) + *s++ = c; + *s = 0; + return s == buf ? (char *)0 : string(buf); +} + +/* gather - read prof.out data from fd */ +static int gather(void) { + int i, nfiles, nfuncs, npoints; + char *files[64]; + + if ((nfiles = getd()) < 0) + return 0; + assert(nfiles < NELEMS(files)); + for (i = 0; i < nfiles; i++) { + if ((files[i] = getstr()) == 0) + return -1; + if (!findfile(files[i])) { + struct file *new; + NEW(new, PERM); + new->name = files[i]; + new->size = new->count = 0; + new->counts = 0; + new->funcs = 0; + new->link = filelist; + filelist = new; + } + } + if ((nfuncs = getd()) < 0) + return -1; + for (i = 0; i < nfuncs; i++) { + struct func *q; + char *name, *file; + int f, x, y, count; + if ((name = getstr()) == 0 || (f = getd()) <= 0 + || (x = getd()) < 0 || (y = getd()) < 0 || (count = getd()) < 0) + return -1; + q = afunction(name, files[f-1], x, y, count); + if ((name = getstr()) == 0 || (file = getstr()) == 0 + || (x = getd()) < 0 || (y = getd()) < 0) + return -1; + if (*name != '?') + acaller(name, file, x, y, count, q); + } + if ((npoints = getd()) < 0) + return -1; + for (i = 0; i < npoints; i++) { + int f, x, y, count; + if ((f = getd()) < 0 || (x = getd()) < 0 || (y = getd()) < 0 + || (count = getd()) < 0) + return -1; + if (f) + apoint(i, files[f-1], x, y, count); + } + return 1; +} + +/* process - read prof.out data from file */ +int process(char *file) { + int more; + + if ((fp = fopen(file, "r")) != NULL) { + struct file *p; + while ((more = gather()) > 0) + ; + fclose(fp); + if (more < 0) + return more; + for (p = filelist; p; p = p->link) + qsort(p->counts, p->count, sizeof *p->counts, + (int (*)(const void *, const void *)) + compare); + + return 1; + } + return 0; +} diff --git a/src/tools/lcc/src/simp.c b/src/tools/lcc/src/simp.c new file mode 100644 index 0000000..ea26ab6 --- /dev/null +++ b/src/tools/lcc/src/simp.c @@ -0,0 +1,587 @@ +#include "c.h" +#include <float.h> + + +#define foldcnst(TYPE,VAR,OP) \ + if (l->op == CNST+TYPE && r->op == CNST+TYPE) \ + return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR) +#define commute(L,R) \ + if (generic(R->op) == CNST && generic(L->op) != CNST) \ + do { Tree t = L; L = R; R = t; } while(0) +#define xfoldcnst(TYPE,VAR,OP,FUNC)\ + if (l->op == CNST+TYPE && r->op == CNST+TYPE\ + && FUNC(l->u.v.VAR,r->u.v.VAR,\ + ty->u.sym->u.limits.min.VAR,\ + ty->u.sym->u.limits.max.VAR, needconst)) \ + return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR) +#define xcvtcnst(FTYPE,SRC,DST,VAR,EXPR) \ + if (l->op == CNST+FTYPE) do {\ + if (!explicitCast\ + && ((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\ + warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, DST);\ + if (needconst\ + || !((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\ + return cnsttree(ty, (EXPR)); } while(0) +#define identity(X,Y,TYPE,VAR,VAL) \ + if (X->op == CNST+TYPE && X->u.v.VAR == VAL) return Y +#define zerofield(OP,TYPE,VAR) \ + if (l->op == FIELD \ + && r->op == CNST+TYPE && r->u.v.VAR == 0)\ + return eqtree(OP, bittree(BAND, l->kids[0],\ + cnsttree(unsignedtype, \ + (unsigned long)fieldmask(l->u.field)<<fieldright(l->u.field))), r) +#define cfoldcnst(TYPE,VAR,OP) \ + if (l->op == CNST+TYPE && r->op == CNST+TYPE) \ + return cnsttree(inttype, (long)(l->u.v.VAR OP r->u.v.VAR)) +#define foldaddp(L,R,RTYPE,VAR) \ + if (L->op == CNST+P && R->op == CNST+RTYPE) { \ + Tree e = tree(CNST+P, ty, NULL, NULL);\ + e->u.v.p = (char *)L->u.v.p + R->u.v.VAR;\ + return e; } +#define ufoldcnst(TYPE,EXP) if (l->op == CNST+TYPE) return EXP +#define sfoldcnst(OP) \ + if (l->op == CNST+U && r->op == CNST+I \ + && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) \ + return cnsttree(ty, (unsigned long)(l->u.v.u OP r->u.v.i)) +#define geu(L,R,V) \ + if (R->op == CNST+U && R->u.v.u == 0) do { \ + warning("result of unsigned comparison is constant\n"); \ + return tree(RIGHT, inttype, root(L), cnsttree(inttype, (long)(V))); } while(0) +#define idempotent(OP) if (l->op == OP) return l->kids[0] + +int needconst; +int explicitCast; +static int addi(long x, long y, long min, long max, int needconst) { + int cond = x == 0 || y == 0 + || (x < 0 && y < 0 && x >= min - y) + || (x < 0 && y > 0) + || (x > 0 && y < 0) + || (x > 0 && y > 0 && x <= max - y); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + + +} + +static int addd(double x, double y, double min, double max, int needconst) { + int cond = x == 0 || y == 0 + || (x < 0 && y < 0 && x >= min - y) + || (x < 0 && y > 0) + || (x > 0 && y < 0) + || (x > 0 && y > 0 && x <= max - y); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + + +} + +static Tree addrtree(Tree e, long n, Type ty) { + Symbol p = e->u.sym, q; + + if (p->scope == GLOBAL + || p->sclass == STATIC || p->sclass == EXTERN) + NEW0(q, PERM); + else + NEW0(q, FUNC); + q->name = stringd(genlabel(1)); + q->sclass = p->sclass; + q->scope = p->scope; + assert(isptr(ty) || isarray(ty)); + q->type = isptr(ty) ? ty->type : ty; + q->temporary = p->temporary; + q->generated = p->generated; + q->addressed = p->addressed; + q->computed = 1; + q->defined = 1; + q->ref = 1; + if (p->scope == GLOBAL + || p->sclass == STATIC || p->sclass == EXTERN) { + if (p->sclass == AUTO) + q->sclass = STATIC; + (*IR->address)(q, p, n); + } else { + Code cp; + addlocal(p); + cp = code(Address); + cp->u.addr.sym = q; + cp->u.addr.base = p; + cp->u.addr.offset = n; + } + e = tree(e->op, ty, NULL, NULL); + e->u.sym = q; + return e; +} + +/* div[id] - return 1 if min <= x/y <= max, 0 otherwise */ +static int divi(long x, long y, long min, long max, int needconst) { + int cond = y != 0 && !(x == min && y == -1); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + + +} + +static int divd(double x, double y, double min, double max, int needconst) { + int cond; + + if (x < 0) x = -x; + if (y < 0) y = -y; + cond = y != 0 && !(y < 1 && x > max*y); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + +} + +/* mul[id] - return 1 if min <= x*y <= max, 0 otherwise */ +static int muli(long x, long y, long min, long max, int needconst) { + int cond = (x > -1 && x <= 1) || (y > -1 && y <= 1) + || (x < 0 && y < 0 && -x <= max/-y) + || (x < 0 && y > 0 && x >= min/y) + || (x > 0 && y < 0 && y >= min/x) + || (x > 0 && y > 0 && x <= max/y); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + + +} + +static int muld(double x, double y, double min, double max, int needconst) { + int cond = (x >= -1 && x <= 1) || (y >= -1 && y <= 1) + || (x < 0 && y < 0 && -x <= max/-y) + || (x < 0 && y > 0 && x >= min/y) + || (x > 0 && y < 0 && y >= min/x) + || (x > 0 && y > 0 && x <= max/y); + if (!cond && needconst) { + warning("overflow in constant expression\n"); + cond = 1; + } + return cond; + + +} +/* sub[id] - return 1 if min <= x-y <= max, 0 otherwise */ +static int subi(long x, long y, long min, long max, int needconst) { + return addi(x, -y, min, max, needconst); +} + +static int subd(double x, double y, double min, double max, int needconst) { + return addd(x, -y, min, max, needconst); +} +Tree constexpr(int tok) { + Tree p; + + needconst++; + p = expr1(tok); + needconst--; + return p; +} + +int intexpr(int tok, int n) { + Tree p = constexpr(tok); + + needconst++; + if (p->op == CNST+I || p->op == CNST+U) + n = cast(p, inttype)->u.v.i; + else + error("integer expression must be constant\n"); + needconst--; + return n; +} +Tree simplify(int op, Type ty, Tree l, Tree r) { + int n; + + if (optype(op) == 0) + op = mkop(op, ty); + switch (op) { + case ADD+U: + foldcnst(U,u,+); + commute(r,l); + identity(r,l,U,u,0); + break; + case ADD+I: + xfoldcnst(I,i,+,addi); + commute(r,l); + identity(r,l,I,i,0); + break; + case CVI+I: + xcvtcnst(I,l->u.v.i,ty,i,(long)extend(l->u.v.i,ty)); + break; + case CVU+I: + if (l->op == CNST+U) { + if (!explicitCast && l->u.v.u > ty->u.sym->u.limits.max.i) + warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, ty); + if (needconst || !(l->u.v.u > ty->u.sym->u.limits.max.i)) + return cnsttree(ty, (long)extend(l->u.v.u,ty)); + } + break; + case CVP+U: + xcvtcnst(P,(unsigned long)l->u.v.p,ty,u,(unsigned long)l->u.v.p); + break; + case CVU+P: + xcvtcnst(U,(void*)l->u.v.u,ty,p,(void*)l->u.v.u); + break; + case CVP+P: + xcvtcnst(P,l->u.v.p,ty,p,l->u.v.p); + break; + case CVI+U: + xcvtcnst(I,l->u.v.i,ty,u,((unsigned long)l->u.v.i)&ones(8*ty->size)); + break; + case CVU+U: + xcvtcnst(U,l->u.v.u,ty,u,l->u.v.u&ones(8*ty->size)); + break; + + case CVI+F: + xcvtcnst(I,l->u.v.i,ty,d,(double)l->u.v.i); + case CVU+F: + xcvtcnst(U,l->u.v.u,ty,d,(double)l->u.v.u); + break; + case CVF+I: + xcvtcnst(F,l->u.v.d,ty,i,(long)l->u.v.d); + break; + case CVF+F: { + float d = 0.0f; + if (l->op == CNST+F) { + if (l->u.v.d < ty->u.sym->u.limits.min.d) + d = ty->u.sym->u.limits.min.d; + else if (l->u.v.d > ty->u.sym->u.limits.max.d) + d = ty->u.sym->u.limits.max.d; + else + d = l->u.v.d; + } + xcvtcnst(F,l->u.v.d,ty,d,(double)d); + break; + } + case BAND+U: + foldcnst(U,u,&); + commute(r,l); + identity(r,l,U,u,ones(8*ty->size)); + if (r->op == CNST+U && r->u.v.u == 0) + return tree(RIGHT, ty, root(l), cnsttree(ty, 0UL)); + break; + case BAND+I: + foldcnst(I,i,&); + commute(r,l); + identity(r,l,I,i,ones(8*ty->size)); + if (r->op == CNST+I && r->u.v.u == 0) + return tree(RIGHT, ty, root(l), cnsttree(ty, 0L)); + break; + + case MUL+U: + commute(l,r); + if (l->op == CNST+U && (n = ispow2(l->u.v.u)) != 0) + return simplify(LSH, ty, r, cnsttree(inttype, (long)n)); + foldcnst(U,u,*); + identity(r,l,U,u,1); + break; + case NE+I: + cfoldcnst(I,i,!=); + commute(r,l); + zerofield(NE,I,i); + break; + + case EQ+I: + cfoldcnst(I,i,==); + commute(r,l); + zerofield(EQ,I,i); + break; + case ADD+P: + foldaddp(l,r,I,i); + foldaddp(l,r,U,u); + foldaddp(r,l,I,i); + foldaddp(r,l,U,u); + commute(r,l); + identity(r,retype(l,ty),I,i,0); + identity(r,retype(l,ty),U,u,0); + if (isaddrop(l->op) + && ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i + && r->u.v.i >= longtype->u.sym->u.limits.min.i) + || (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i))) + return addrtree(l, cast(r, longtype)->u.v.i, ty); + if (l->op == ADD+P && isaddrop(l->kids[1]->op) + && ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i + && r->u.v.i >= longtype->u.sym->u.limits.min.i) + || (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i))) + return simplify(ADD+P, ty, l->kids[0], + addrtree(l->kids[1], cast(r, longtype)->u.v.i, ty)); + if ((l->op == ADD+I || l->op == SUB+I) + && l->kids[1]->op == CNST+I && isaddrop(r->op)) + return simplify(ADD+P, ty, l->kids[0], + simplify(generic(l->op)+P, ty, r, l->kids[1])); + if (l->op == ADD+P && generic(l->kids[1]->op) == CNST + && generic(r->op) == CNST) + return simplify(ADD+P, ty, l->kids[0], + simplify(ADD, l->kids[1]->type, l->kids[1], r)); + if (l->op == ADD+I && generic(l->kids[1]->op) == CNST + && r->op == ADD+P && generic(r->kids[1]->op) == CNST) + return simplify(ADD+P, ty, l->kids[0], + simplify(ADD+P, ty, r->kids[0], + simplify(ADD, r->kids[1]->type, l->kids[1], r->kids[1]))); + if (l->op == RIGHT && l->kids[1]) + return tree(RIGHT, ty, l->kids[0], + simplify(ADD+P, ty, l->kids[1], r)); + else if (l->op == RIGHT && l->kids[0]) + return tree(RIGHT, ty, + simplify(ADD+P, ty, l->kids[0], r), NULL); + break; + + case ADD+F: + xfoldcnst(F,d,+,addd); + commute(r,l); + break; + case AND+I: + op = AND; + ufoldcnst(I,l->u.v.i ? cond(r) : l); /* 0&&r => 0, 1&&r => r */ + break; + case OR+I: + op = OR; + /* 0||r => r, 1||r => 1 */ + ufoldcnst(I,l->u.v.i ? cnsttree(ty, 1L) : cond(r)); + break; + case BCOM+I: + ufoldcnst(I,cnsttree(ty, (long)extend((~l->u.v.i)&ones(8*ty->size), ty))); + idempotent(BCOM+U); + break; + case BCOM+U: + ufoldcnst(U,cnsttree(ty, (unsigned long)((~l->u.v.u)&ones(8*ty->size)))); + idempotent(BCOM+U); + break; + case BOR+U: + foldcnst(U,u,|); + commute(r,l); + identity(r,l,U,u,0); + break; + case BOR+I: + foldcnst(I,i,|); + commute(r,l); + identity(r,l,I,i,0); + break; + case BXOR+U: + foldcnst(U,u,^); + commute(r,l); + identity(r,l,U,u,0); + break; + case BXOR+I: + foldcnst(I,i,^); + commute(r,l); + identity(r,l,I,i,0); + break; + case DIV+F: + xfoldcnst(F,d,/,divd); + break; + case DIV+I: + identity(r,l,I,i,1); + if ((r->op == CNST+I && r->u.v.i == 0) + || (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i + && r->op == CNST+I && r->u.v.i == -1)) + break; + xfoldcnst(I,i,/,divi); + break; + case DIV+U: + identity(r,l,U,u,1); + if (r->op == CNST+U && r->u.v.u == 0) + break; + if (r->op == CNST+U && (n = ispow2(r->u.v.u)) != 0) + return simplify(RSH, ty, l, cnsttree(inttype, (long)n)); + foldcnst(U,u,/); + break; + case EQ+F: + cfoldcnst(F,d,==); + commute(r,l); + break; + case EQ+U: + cfoldcnst(U,u,==); + commute(r,l); + zerofield(EQ,U,u); + break; + case GE+F: cfoldcnst(F,d,>=); break; + case GE+I: cfoldcnst(I,i,>=); break; + case GE+U: + geu(l,r,1); /* l >= 0 => (l,1) */ + cfoldcnst(U,u,>=); + if (l->op == CNST+U && l->u.v.u == 0) /* 0 >= r => r == 0 */ + return eqtree(EQ, r, l); + break; + case GT+F: cfoldcnst(F,d, >); break; + case GT+I: cfoldcnst(I,i, >); break; + case GT+U: + geu(r,l,0); /* 0 > r => (r,0) */ + cfoldcnst(U,u, >); + if (r->op == CNST+U && r->u.v.u == 0) /* l > 0 => l != 0 */ + return eqtree(NE, l, r); + break; + case LE+F: cfoldcnst(F,d,<=); break; + case LE+I: cfoldcnst(I,i,<=); break; + case LE+U: + geu(r,l,1); /* 0 <= r => (r,1) */ + cfoldcnst(U,u,<=); + if (r->op == CNST+U && r->u.v.u == 0) /* l <= 0 => l == 0 */ + return eqtree(EQ, l, r); + break; + case LSH+I: + identity(r,l,I,i,0); + if (l->op == CNST+I && r->op == CNST+I + && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size + && muli(l->u.v.i, 1<<r->u.v.i, ty->u.sym->u.limits.min.i, ty->u.sym->u.limits.max.i, needconst)) + return cnsttree(ty, (long)(l->u.v.i<<r->u.v.i)); + if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { + warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); + break; + } + + break; + case LSH+U: + identity(r,l,I,i,0); + sfoldcnst(<<); + if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { + warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); + break; + } + + break; + + case LT+F: cfoldcnst(F,d, <); break; + case LT+I: cfoldcnst(I,i, <); break; + case LT+U: + geu(l,r,0); /* l < 0 => (l,0) */ + cfoldcnst(U,u, <); + if (l->op == CNST+U && l->u.v.u == 0) /* 0 < r => r != 0 */ + return eqtree(NE, r, l); + break; + case MOD+I: + if (r->op == CNST+I && r->u.v.i == 1) /* l%1 => (l,0) */ + return tree(RIGHT, ty, root(l), cnsttree(ty, 0L)); + if ((r->op == CNST+I && r->u.v.i == 0) + || (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i + && r->op == CNST+I && r->u.v.i == -1)) + break; + xfoldcnst(I,i,%,divi); + break; + case MOD+U: + if (r->op == CNST+U && ispow2(r->u.v.u)) /* l%2^n => l&(2^n-1) */ + return bittree(BAND, l, cnsttree(ty, r->u.v.u - 1)); + if (r->op == CNST+U && r->u.v.u == 0) + break; + foldcnst(U,u,%); + break; + case MUL+F: + xfoldcnst(F,d,*,muld); + commute(l,r); + break; + case MUL+I: + commute(l,r); + xfoldcnst(I,i,*,muli); + if (l->op == CNST+I && r->op == ADD+I && r->kids[1]->op == CNST+I) + /* c1*(x + c2) => c1*x + c1*c2 */ + return simplify(ADD, ty, simplify(MUL, ty, l, r->kids[0]), + simplify(MUL, ty, l, r->kids[1])); + if (l->op == CNST+I && r->op == SUB+I && r->kids[1]->op == CNST+I) + /* c1*(x - c2) => c1*x - c1*c2 */ + return simplify(SUB, ty, simplify(MUL, ty, l, r->kids[0]), + simplify(MUL, ty, l, r->kids[1])); + if (l->op == CNST+I && l->u.v.i > 0 && (n = ispow2(l->u.v.i)) != 0) + /* 2^n * r => r<<n */ + return simplify(LSH, ty, r, cnsttree(inttype, (long)n)); + identity(r,l,I,i,1); + break; + case NE+F: + cfoldcnst(F,d,!=); + commute(r,l); + break; + case NE+U: + cfoldcnst(U,u,!=); + commute(r,l); + zerofield(NE,U,u); + break; + case NEG+F: + ufoldcnst(F,cnsttree(ty, -l->u.v.d)); + idempotent(NEG+F); + break; + case NEG+I: + if (l->op == CNST+I) { + if (needconst && l->u.v.i == ty->u.sym->u.limits.min.i) + warning("overflow in constant expression\n"); + if (needconst || l->u.v.i != ty->u.sym->u.limits.min.i) + return cnsttree(ty, -l->u.v.i); + } + idempotent(NEG+I); + break; + case NOT+I: + op = NOT; + ufoldcnst(I,cnsttree(ty, !l->u.v.i)); + break; + case RSH+I: + identity(r,l,I,i,0); + if (l->op == CNST+I && r->op == CNST+I + && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) { + long n = l->u.v.i>>r->u.v.i; + if (l->u.v.i < 0) + n |= ~0UL<<(8*l->type->size - r->u.v.i); + return cnsttree(ty, n); + } + if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { + warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); + break; + } + + break; + case RSH+U: + identity(r,l,I,i,0); + sfoldcnst(>>); + if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { + warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); + break; + } + + break; + case SUB+F: + xfoldcnst(F,d,-,subd); + break; + case SUB+I: + xfoldcnst(I,i,-,subi); + identity(r,l,I,i,0); + break; + case SUB+U: + foldcnst(U,u,-); + identity(r,l,U,u,0); + break; + case SUB+P: + if (l->op == CNST+P && r->op == CNST+P) + return cnsttree(ty, (long)((char *)l->u.v.p - (char *)r->u.v.p)); + if (r->op == CNST+I || r->op == CNST+U) + return simplify(ADD, ty, l, + cnsttree(inttype, r->op == CNST+I ? -r->u.v.i : -(long)r->u.v.u)); + if (isaddrop(l->op) && r->op == ADD+I && r->kids[1]->op == CNST+I) + /* l - (x + c) => l-c - x */ + return simplify(SUB, ty, + simplify(SUB, ty, l, r->kids[1]), r->kids[0]); + break; + default:assert(0); + } + return tree(op, ty, l, r); +} +/* ispow2 - if u > 1 && u == 2^n, return n, otherwise return 0 */ +int ispow2(unsigned long u) { + int n; + + if (u > 1 && (u&(u-1)) == 0) + for (n = 0; u; u >>= 1, n++) + if (u&1) + return n; + return 0; +} + diff --git a/src/tools/lcc/src/stmt.c b/src/tools/lcc/src/stmt.c new file mode 100644 index 0000000..9c3bdbe --- /dev/null +++ b/src/tools/lcc/src/stmt.c @@ -0,0 +1,696 @@ +#include "c.h" + + +#define SWSIZE 512 + +#define den(i,j) ((j-buckets[i]+1.0)/(v[j]-v[buckets[i]]+1)) + +struct code codehead = { Start }; +Code codelist = &codehead; +float density = 0.5; +Table stmtlabs; + +static int foldcond(Tree e1, Tree e2); +static void caselabel(Swtch, long, int); +static void cmp(int, Symbol, long, int); +static Tree conditional(int); +static void dostmt(int, Swtch, int); +static int equal(Symbol, Symbol); +static void forstmt(int, Swtch, int); +static void ifstmt(int, int, Swtch, int); +static Symbol localaddr(Tree); +static void stmtlabel(void); +static void swstmt(int, int, int); +static void whilestmt(int, Swtch, int); +Code code(int kind) { + Code cp; + + if (!reachable(kind)) + warning("unreachable code\n"); + + NEW(cp, FUNC); + cp->kind = kind; + cp->prev = codelist; + cp->next = NULL; + codelist->next = cp; + codelist = cp; + return cp; +} +int reachable(int kind) { + + if (kind > Start) { + Code cp; + for (cp = codelist; cp->kind < Label; ) + cp = cp->prev; + if (cp->kind == Jump || cp->kind == Switch) + return 0; + } + return 1; +} +void addlocal(Symbol p) { + if (!p->defined) { + code(Local)->u.var = p; + p->defined = 1; + p->scope = level; + } +} +void definept(Coordinate *p) { + Code cp = code(Defpoint); + + cp->u.point.src = p ? *p : src; + cp->u.point.point = npoints; + if (ncalled > 0) { + int n = findcount(cp->u.point.src.file, + cp->u.point.src.x, cp->u.point.src.y); + if (n > 0) + refinc = (float)n/ncalled; + } + if (glevel > 2) locus(identifiers, &cp->u.point.src); + if (events.points && reachable(Gen)) + { + Tree e = NULL; + apply(events.points, &cp->u.point.src, &e); + if (e) + listnodes(e, 0, 0); + } +} +void statement(int loop, Swtch swp, int lev) { + float ref = refinc; + + if (Aflag >= 2 && lev == 15) + warning("more than 15 levels of nested statements\n"); + switch (t) { + case IF: ifstmt(genlabel(2), loop, swp, lev + 1); + break; + case WHILE: whilestmt(genlabel(3), swp, lev + 1); break; + case DO: dostmt(genlabel(3), swp, lev + 1); expect(';'); + break; + + case FOR: forstmt(genlabel(4), swp, lev + 1); + break; + case BREAK: walk(NULL, 0, 0); + definept(NULL); + if (swp && swp->lab > loop) + branch(swp->lab + 1); + else if (loop) + branch(loop + 2); + else + error("illegal break statement\n"); + t = gettok(); expect(';'); + break; + + case CONTINUE: walk(NULL, 0, 0); + definept(NULL); + if (loop) + branch(loop + 1); + else + error("illegal continue statement\n"); + t = gettok(); expect(';'); + break; + + case SWITCH: swstmt(loop, genlabel(2), lev + 1); + break; + case CASE: { + int lab = genlabel(1); + if (swp == NULL) + error("illegal case label\n"); + definelab(lab); + while (t == CASE) { + static char stop[] = { IF, ID, 0 }; + Tree p; + t = gettok(); + p = constexpr(0); + if (generic(p->op) == CNST && isint(p->type)) { + if (swp) { + needconst++; + p = cast(p, swp->sym->type); + if (p->type->op == UNSIGNED) + p->u.v.i = extend(p->u.v.u, p->type); + needconst--; + caselabel(swp, p->u.v.i, lab); + } + } else + error("case label must be a constant integer expression\n"); + + test(':', stop); + } + statement(loop, swp, lev); + } break; + case DEFAULT: if (swp == NULL) + error("illegal default label\n"); + else if (swp->deflab) + error("extra default label\n"); + else { + swp->deflab = findlabel(swp->lab); + definelab(swp->deflab->u.l.label); + } + t = gettok(); + expect(':'); + statement(loop, swp, lev); break; + case RETURN: { + Type rty = freturn(cfunc->type); + t = gettok(); + definept(NULL); + if (t != ';') + if (rty == voidtype) { + error("extraneous return value\n"); + expr(0); + retcode(NULL); + } else + retcode(expr(0)); + else { + if (rty != voidtype) + warning("missing return value\n"); + retcode(NULL); + } + branch(cfunc->u.f.label); + } expect(';'); + break; + + case '{': compound(loop, swp, lev + 1); break; + case ';': definept(NULL); t = gettok(); break; + case GOTO: walk(NULL, 0, 0); + definept(NULL); + t = gettok(); + if (t == ID) { + Symbol p = lookup(token, stmtlabs); + if (p == NULL) { + p = install(token, &stmtlabs, 0, FUNC); + p->scope = LABELS; + p->u.l.label = genlabel(1); + p->src = src; + } + use(p, src); + branch(p->u.l.label); + t = gettok(); + } else + error("missing label in goto\n"); expect(';'); + break; + + case ID: if (getchr() == ':') { + stmtlabel(); + statement(loop, swp, lev); + break; + } + default: definept(NULL); + if (kind[t] != ID) { + error("unrecognized statement\n"); + t = gettok(); + } else { + Tree e = expr0(0); + listnodes(e, 0, 0); + if (nodecount == 0 || nodecount > 200) + walk(NULL, 0, 0); + else if (glevel) walk(NULL, 0, 0); + deallocate(STMT); + } expect(';'); + break; + + } + if (kind[t] != IF && kind[t] != ID + && t != '}' && t != EOI) { + static char stop[] = { IF, ID, '}', 0 }; + error("illegal statement termination\n"); + skipto(0, stop); + } + refinc = ref; +} + +static void ifstmt(int lab, int loop, Swtch swp, int lev) { + t = gettok(); + expect('('); + definept(NULL); + walk(conditional(')'), 0, lab); + refinc /= 2.0; + statement(loop, swp, lev); + if (t == ELSE) { + branch(lab + 1); + t = gettok(); + definelab(lab); + statement(loop, swp, lev); + if (findlabel(lab + 1)->ref) + definelab(lab + 1); + } else + definelab(lab); +} +static Tree conditional(int tok) { + Tree p = expr(tok); + + if (Aflag > 1 && isfunc(p->type)) + warning("%s used in a conditional expression\n", + funcname(p)); + return cond(p); +} +static void stmtlabel(void) { + Symbol p = lookup(token, stmtlabs); + + if (p == NULL) { + p = install(token, &stmtlabs, 0, FUNC); + p->scope = LABELS; + p->u.l.label = genlabel(1); + p->src = src; + } + if (p->defined) + error("redefinition of label `%s' previously defined at %w\n", p->name, &p->src); + + p->defined = 1; + definelab(p->u.l.label); + t = gettok(); + expect(':'); +} +static void forstmt(int lab, Swtch swp, int lev) { + int once = 0; + Tree e1 = NULL, e2 = NULL, e3 = NULL; + Coordinate pt2, pt3; + + t = gettok(); + expect('('); + definept(NULL); + if (kind[t] == ID) + e1 = texpr(expr0, ';', FUNC); + else + expect(';'); + walk(e1, 0, 0); + pt2 = src; + refinc *= 10.0; + if (kind[t] == ID) + e2 = texpr(conditional, ';', FUNC); + else + expect(';'); + pt3 = src; + if (kind[t] == ID) + e3 = texpr(expr0, ')', FUNC); + else { + static char stop[] = { IF, ID, '}', 0 }; + test(')', stop); + } + if (e2) { + once = foldcond(e1, e2); + if (!once) + branch(lab + 3); + } + definelab(lab); + statement(lab, swp, lev); + definelab(lab + 1); + definept(&pt3); + if (e3) + walk(e3, 0, 0); + if (e2) { + if (!once) + definelab(lab + 3); + definept(&pt2); + walk(e2, lab, 0); + } else { + definept(&pt2); + branch(lab); + } + if (findlabel(lab + 2)->ref) + definelab(lab + 2); +} +static void swstmt(int loop, int lab, int lev) { + Tree e; + struct swtch sw; + Code head, tail; + + t = gettok(); + expect('('); + definept(NULL); + e = expr(')'); + if (!isint(e->type)) { + error("illegal type `%t' in switch expression\n", + e->type); + e = retype(e, inttype); + } + e = cast(e, promote(e->type)); + if (generic(e->op) == INDIR && isaddrop(e->kids[0]->op) + && e->kids[0]->u.sym->type == e->type + && !isvolatile(e->kids[0]->u.sym->type)) { + sw.sym = e->kids[0]->u.sym; + walk(NULL, 0, 0); + } else { + sw.sym = genident(REGISTER, e->type, level); + addlocal(sw.sym); + walk(asgn(sw.sym, e), 0, 0); + } + head = code(Switch); + sw.lab = lab; + sw.deflab = NULL; + sw.ncases = 0; + sw.size = SWSIZE; + sw.values = newarray(SWSIZE, sizeof *sw.values, FUNC); + sw.labels = newarray(SWSIZE, sizeof *sw.labels, FUNC); + refinc /= 10.0; + statement(loop, &sw, lev); + if (sw.deflab == NULL) { + sw.deflab = findlabel(lab); + definelab(lab); + if (sw.ncases == 0) + warning("switch statement with no cases\n"); + } + if (findlabel(lab + 1)->ref) + definelab(lab + 1); + tail = codelist; + codelist = head->prev; + codelist->next = head->prev = NULL; + if (sw.ncases > 0) + swgen(&sw); + branch(lab); + head->next->prev = codelist; + codelist->next = head->next; + codelist = tail; +} +static void caselabel(Swtch swp, long val, int lab) { + int k; + + if (swp->ncases >= swp->size) + { + long *vals = swp->values; + Symbol *labs = swp->labels; + swp->size *= 2; + swp->values = newarray(swp->size, sizeof *swp->values, FUNC); + swp->labels = newarray(swp->size, sizeof *swp->labels, FUNC); + for (k = 0; k < swp->ncases; k++) { + swp->values[k] = vals[k]; + swp->labels[k] = labs[k]; + } + } + k = swp->ncases; + for ( ; k > 0 && swp->values[k-1] >= val; k--) { + swp->values[k] = swp->values[k-1]; + swp->labels[k] = swp->labels[k-1]; + } + if (k < swp->ncases && swp->values[k] == val) + error("duplicate case label `%d'\n", val); + swp->values[k] = val; + swp->labels[k] = findlabel(lab); + ++swp->ncases; + if (Aflag >= 2 && swp->ncases == 258) + warning("more than 257 cases in a switch\n"); +} +void swgen(Swtch swp) { + int *buckets, k, n; + long *v = swp->values; + + buckets = newarray(swp->ncases + 1, + sizeof *buckets, FUNC); + for (n = k = 0; k < swp->ncases; k++, n++) { + buckets[n] = k; + while (n > 0 && den(n-1, k) >= density) + n--; + } + buckets[n] = swp->ncases; + swcode(swp, buckets, 0, n - 1); +} +void swcode(Swtch swp, int b[], int lb, int ub) { + int hilab, lolab, l, u, k = (lb + ub)/2; + long *v = swp->values; + + if (k > lb && k < ub) { + lolab = genlabel(1); + hilab = genlabel(1); + } else if (k > lb) { + lolab = genlabel(1); + hilab = swp->deflab->u.l.label; + } else if (k < ub) { + lolab = swp->deflab->u.l.label; + hilab = genlabel(1); + } else + lolab = hilab = swp->deflab->u.l.label; + l = b[k]; + u = b[k+1] - 1; + if (u - l + 1 <= 3) + { + int i; + for (i = l; i <= u; i++) + cmp(EQ, swp->sym, v[i], swp->labels[i]->u.l.label); + if (k > lb && k < ub) + cmp(GT, swp->sym, v[u], hilab); + else if (k > lb) + cmp(GT, swp->sym, v[u], hilab); + else if (k < ub) + cmp(LT, swp->sym, v[l], lolab); + else + assert(lolab == hilab), + branch(lolab); + walk(NULL, 0, 0); + } + else { + Tree e; + Type ty = signedint(swp->sym->type); + Symbol table = genident(STATIC, + array(voidptype, u - l + 1, 0), GLOBAL); + (*IR->defsymbol)(table); + if (!isunsigned(swp->sym->type) || v[l] != 0) + cmp(LT, swp->sym, v[l], lolab); + cmp(GT, swp->sym, v[u], hilab); + e = (*optree['-'])(SUB, cast(idtree(swp->sym), ty), cnsttree(ty, v[l])); + if (e->type->size < unsignedptr->size) + e = cast(e, unsignedlong); + walk(tree(JUMP, voidtype, + rvalue((*optree['+'])(ADD, pointer(idtree(table)), e)), NULL), + 0, 0); + code(Switch); + codelist->u.swtch.table = table; + codelist->u.swtch.sym = swp->sym; + codelist->u.swtch.deflab = swp->deflab; + codelist->u.swtch.size = u - l + 1; + codelist->u.swtch.values = &v[l]; + codelist->u.swtch.labels = &swp->labels[l]; + if (v[u] - v[l] + 1 >= 10000) + warning("switch generates a huge table\n"); + } + if (k > lb) { + assert(lolab != swp->deflab->u.l.label); + definelab(lolab); + swcode(swp, b, lb, k - 1); + } + if (k < ub) { + assert(hilab != swp->deflab->u.l.label); + definelab(hilab); + swcode(swp, b, k + 1, ub); + } +} +static void cmp(int op, Symbol p, long n, int lab) { + Type ty = signedint(p->type); + + listnodes(eqtree(op, + cast(idtree(p), ty), + cnsttree(ty, n)), + lab, 0); +} +void retcode(Tree p) { + Type ty; + + if (p == NULL) { + if (events.returns) + apply(events.returns, cfunc, NULL); + return; + } + p = pointer(p); + ty = assign(freturn(cfunc->type), p); + if (ty == NULL) { + error("illegal return type; found `%t' expected `%t'\n", + p->type, freturn(cfunc->type)); + return; + } + p = cast(p, ty); + if (retv) + { + if (iscallb(p)) + p = tree(RIGHT, p->type, + tree(CALL+B, p->type, + p->kids[0]->kids[0], idtree(retv)), + rvalue(idtree(retv))); + else + p = asgntree(ASGN, rvalue(idtree(retv)), p); + walk(p, 0, 0); + if (events.returns) + apply(events.returns, cfunc, rvalue(idtree(retv))); + return; + } + if (events.returns) + { + Symbol t1 = genident(AUTO, p->type, level); + addlocal(t1); + walk(asgn(t1, p), 0, 0); + apply(events.returns, cfunc, idtree(t1)); + p = idtree(t1); + } + if (!isfloat(p->type)) + p = cast(p, promote(p->type)); + if (isptr(p->type)) + { + Symbol q = localaddr(p); + if (q && (q->computed || q->generated)) + warning("pointer to a %s is an illegal return value\n", + q->scope == PARAM ? "parameter" : "local"); + else if (q) + warning("pointer to %s `%s' is an illegal return value\n", + q->scope == PARAM ? "parameter" : "local", q->name); + } + walk(tree(mkop(RET,p->type), p->type, p, NULL), 0, 0); +} +void definelab(int lab) { + Code cp; + Symbol p = findlabel(lab); + + assert(lab); + walk(NULL, 0, 0); + code(Label)->u.forest = newnode(LABEL+V, NULL, NULL, p); + for (cp = codelist->prev; cp->kind <= Label; ) + cp = cp->prev; + while ( cp->kind == Jump + && cp->u.forest->kids[0] + && specific(cp->u.forest->kids[0]->op) == ADDRG+P + && cp->u.forest->kids[0]->syms[0] == p) { + assert(cp->u.forest->kids[0]->syms[0]->u.l.label == lab); + p->ref--; + assert(cp->next); + assert(cp->prev); + cp->prev->next = cp->next; + cp->next->prev = cp->prev; + cp = cp->prev; + while (cp->kind <= Label) + cp = cp->prev; + } +} +Node jump(int lab) { + Symbol p = findlabel(lab); + + p->ref++; + return newnode(JUMP+V, newnode(ADDRG+ttob(voidptype), NULL, NULL, p), + NULL, NULL); +} +void branch(int lab) { + Code cp; + Symbol p = findlabel(lab); + + assert(lab); + walk(NULL, 0, 0); + code(Label)->u.forest = jump(lab); + for (cp = codelist->prev; cp->kind < Label; ) + cp = cp->prev; + while ( cp->kind == Label + && cp->u.forest->op == LABEL+V + && !equal(cp->u.forest->syms[0], p)) { + equatelab(cp->u.forest->syms[0], p); + assert(cp->next); + assert(cp->prev); + cp->prev->next = cp->next; + cp->next->prev = cp->prev; + cp = cp->prev; + while (cp->kind < Label) + cp = cp->prev; + } + if (cp->kind == Jump || cp->kind == Switch) { + p->ref--; + codelist->prev->next = NULL; + codelist = codelist->prev; + } else { + codelist->kind = Jump; + if (cp->kind == Label + && cp->u.forest->op == LABEL+V + && equal(cp->u.forest->syms[0], p)) + warning("source code specifies an infinite loop"); + } +} +void equatelab(Symbol old, Symbol new) { + assert(old->u.l.equatedto == NULL); + old->u.l.equatedto = new; + new->ref++; +} +static int equal(Symbol lprime, Symbol dst) { + assert(dst && lprime); + for ( ; dst; dst = dst->u.l.equatedto) + if (lprime == dst) + return 1; + return 0; +} +/* dostmt - do statement while ( expression ) */ +static void dostmt(int lab, Swtch swp, int lev) { + refinc *= 10.0; + t = gettok(); + definelab(lab); + statement(lab, swp, lev); + definelab(lab + 1); + expect(WHILE); + expect('('); + definept(NULL); + walk(conditional(')'), lab, 0); + if (findlabel(lab + 2)->ref) + definelab(lab + 2); +} + +/* foldcond - check if initial test in for(e1;e2;e3) S is necessary */ +static int foldcond(Tree e1, Tree e2) { + int op = generic(e2->op); + Symbol v; + + if (e1 == 0 || e2 == 0) + return 0; + if (generic(e1->op) == ASGN && isaddrop(e1->kids[0]->op) + && generic(e1->kids[1]->op) == CNST) { + v = e1->kids[0]->u.sym; + e1 = e1->kids[1]; + } else + return 0; + if ((op==LE || op==LT || op==EQ || op==NE || op==GT || op==GE) + && generic(e2->kids[0]->op) == INDIR + && e2->kids[0]->kids[0]->u.sym == v + && e2->kids[1]->op == e1->op) { + e1 = simplify(op, e2->type, e1, e2->kids[1]); + if (e1->op == CNST+I) + return e1->u.v.i; + } + return 0; +} + +/* localaddr - returns q if p yields the address of local/parameter q; otherwise returns 0 */ +static Symbol localaddr(Tree p) { + if (p == NULL) + return NULL; + switch (generic(p->op)) { + case INDIR: case CALL: case ARG: + return NULL; + case ADDRL: case ADDRF: + return p->u.sym; + case RIGHT: case ASGN: + if (p->kids[1]) + return localaddr(p->kids[1]); + return localaddr(p->kids[0]); + case COND: { + Symbol q; + assert(p->kids[1] && p->kids[1]->op == RIGHT); + if ((q = localaddr(p->kids[1]->kids[0])) != NULL) + return q; + return localaddr(p->kids[1]->kids[1]); + } + default: { + Symbol q; + if (p->kids[0] && (q = localaddr(p->kids[0])) != NULL) + return q; + return localaddr(p->kids[1]); + } + } +} + +/* whilestmt - while ( expression ) statement */ +static void whilestmt(int lab, Swtch swp, int lev) { + Coordinate pt; + Tree e; + + refinc *= 10.0; + t = gettok(); + expect('('); + walk(NULL, 0, 0); + pt = src; + e = texpr(conditional, ')', FUNC); + branch(lab + 1); + definelab(lab); + statement(lab, swp, lev); + definelab(lab + 1); + definept(&pt); + walk(e, lab, 0); + if (findlabel(lab + 2)->ref) + definelab(lab + 2); +} diff --git a/src/tools/lcc/src/string.c b/src/tools/lcc/src/string.c new file mode 100644 index 0000000..73cfc85 --- /dev/null +++ b/src/tools/lcc/src/string.c @@ -0,0 +1,122 @@ +#include "c.h" + + +static struct string { + char *str; + int len; + struct string *link; +} *buckets[1024]; +static int scatter[] = { /* map characters to random values */ + 2078917053, 143302914, 1027100827, 1953210302, 755253631, + 2002600785, 1405390230, 45248011, 1099951567, 433832350, + 2018585307, 438263339, 813528929, 1703199216, 618906479, + 573714703, 766270699, 275680090, 1510320440, 1583583926, + 1723401032, 1965443329, 1098183682, 1636505764, 980071615, + 1011597961, 643279273, 1315461275, 157584038, 1069844923, + 471560540, 89017443, 1213147837, 1498661368, 2042227746, + 1968401469, 1353778505, 1300134328, 2013649480, 306246424, + 1733966678, 1884751139, 744509763, 400011959, 1440466707, + 1363416242, 973726663, 59253759, 1639096332, 336563455, + 1642837685, 1215013716, 154523136, 593537720, 704035832, + 1134594751, 1605135681, 1347315106, 302572379, 1762719719, + 269676381, 774132919, 1851737163, 1482824219, 125310639, + 1746481261, 1303742040, 1479089144, 899131941, 1169907872, + 1785335569, 485614972, 907175364, 382361684, 885626931, + 200158423, 1745777927, 1859353594, 259412182, 1237390611, + 48433401, 1902249868, 304920680, 202956538, 348303940, + 1008956512, 1337551289, 1953439621, 208787970, 1640123668, + 1568675693, 478464352, 266772940, 1272929208, 1961288571, + 392083579, 871926821, 1117546963, 1871172724, 1771058762, + 139971187, 1509024645, 109190086, 1047146551, 1891386329, + 994817018, 1247304975, 1489680608, 706686964, 1506717157, + 579587572, 755120366, 1261483377, 884508252, 958076904, + 1609787317, 1893464764, 148144545, 1415743291, 2102252735, + 1788268214, 836935336, 433233439, 2055041154, 2109864544, + 247038362, 299641085, 834307717, 1364585325, 23330161, + 457882831, 1504556512, 1532354806, 567072918, 404219416, + 1276257488, 1561889936, 1651524391, 618454448, 121093252, + 1010757900, 1198042020, 876213618, 124757630, 2082550272, + 1834290522, 1734544947, 1828531389, 1982435068, 1002804590, + 1783300476, 1623219634, 1839739926, 69050267, 1530777140, + 1802120822, 316088629, 1830418225, 488944891, 1680673954, + 1853748387, 946827723, 1037746818, 1238619545, 1513900641, + 1441966234, 367393385, 928306929, 946006977, 985847834, + 1049400181, 1956764878, 36406206, 1925613800, 2081522508, + 2118956479, 1612420674, 1668583807, 1800004220, 1447372094, + 523904750, 1435821048, 923108080, 216161028, 1504871315, + 306401572, 2018281851, 1820959944, 2136819798, 359743094, + 1354150250, 1843084537, 1306570817, 244413420, 934220434, + 672987810, 1686379655, 1301613820, 1601294739, 484902984, + 139978006, 503211273, 294184214, 176384212, 281341425, + 228223074, 147857043, 1893762099, 1896806882, 1947861263, + 1193650546, 273227984, 1236198663, 2116758626, 489389012, + 593586330, 275676551, 360187215, 267062626, 265012701, + 719930310, 1621212876, 2108097238, 2026501127, 1865626297, + 894834024, 552005290, 1404522304, 48964196, 5816381, + 1889425288, 188942202, 509027654, 36125855, 365326415, + 790369079, 264348929, 513183458, 536647531, 13672163, + 313561074, 1730298077, 286900147, 1549759737, 1699573055, + 776289160, 2143346068, 1975249606, 1136476375, 262925046, + 92778659, 1856406685, 1884137923, 53392249, 1735424165, + 1602280572 +}; +char *string(const char *str) { + const char *s; + + for (s = str; *s; s++) + ; + return stringn(str, s - str); +} +char *stringd(long n) { + char str[25], *s = str + sizeof (str); + unsigned long m; + + if (n == LONG_MIN) + m = (unsigned long)LONG_MAX + 1; + else if (n < 0) + m = -n; + else + m = n; + do + *--s = m%10 + '0'; + while ((m /= 10) != 0); + if (n < 0) + *--s = '-'; + return stringn(s, str + sizeof (str) - s); +} +char *stringn(const char *str, int len) { + int i; + unsigned int h; + const char *end; + struct string *p; + + assert(str); + for (h = 0, i = len, end = str; i > 0; i--) + h = (h<<1) + scatter[*(unsigned char *)end++]; + h &= NELEMS(buckets)-1; + for (p = buckets[h]; p; p = p->link) + if (len == p->len) { + const char *s1 = str; + char *s2 = p->str; + do { + if (s1 == end) + return p->str; + } while (*s1++ == *s2++); + } + { + static char *next, *strlimit; + if (len + 1 >= strlimit - next) { + int n = len + 4*1024; + next = allocate(n, PERM); + strlimit = next + n; + } + NEW(p, PERM); + p->len = len; + for (p->str = next; str < end; ) + *next++ = *str++; + *next++ = 0; + p->link = buckets[h]; + buckets[h] = p; + return p->str; + } +} diff --git a/src/tools/lcc/src/sym.c b/src/tools/lcc/src/sym.c new file mode 100644 index 0000000..2a1cfeb --- /dev/null +++ b/src/tools/lcc/src/sym.c @@ -0,0 +1,314 @@ +#include "c.h" +#include <stdio.h> + + +#define equalp(x) v.x == p->sym.u.c.v.x + +struct table { + int level; + Table previous; + struct entry { + struct symbol sym; + struct entry *link; + } *buckets[256]; + Symbol all; +}; +#define HASHSIZE NELEMS(((Table)0)->buckets) +static struct table + cns = { CONSTANTS }, + ext = { GLOBAL }, + ids = { GLOBAL }, + tys = { GLOBAL }; +Table constants = &cns; +Table externals = &ext; +Table identifiers = &ids; +Table globals = &ids; +Table types = &tys; +Table labels; +int level = GLOBAL; +static int tempid; +List loci, symbols; + +Table table(Table tp, int level) { + Table new; + + NEW0(new, FUNC); + new->previous = tp; + new->level = level; + if (tp) + new->all = tp->all; + return new; +} +void foreach(Table tp, int lev, void (*apply)(Symbol, void *), void *cl) { + assert(tp); + while (tp && tp->level > lev) + tp = tp->previous; + if (tp && tp->level == lev) { + Symbol p; + Coordinate sav; + sav = src; + for (p = tp->all; p && p->scope == lev; p = p->up) { + src = p->src; + (*apply)(p, cl); + } + src = sav; + } +} +void enterscope(void) { + if (++level == LOCAL) + tempid = 0; +} +void exitscope(void) { + rmtypes(level); + if (types->level == level) + types = types->previous; + if (identifiers->level == level) { + if (Aflag >= 2) { + int n = 0; + Symbol p; + for (p = identifiers->all; p && p->scope == level; p = p->up) + if (++n > 127) { + warning("more than 127 identifiers declared in a block\n"); + break; + } + } + identifiers = identifiers->previous; + } + assert(level >= GLOBAL); + --level; +} +Symbol install(const char *name, Table *tpp, int level, int arena) { + Table tp = *tpp; + struct entry *p; + unsigned h = (unsigned long)name&(HASHSIZE-1); + + assert(level == 0 || level >= tp->level); + if (level > 0 && tp->level < level) + tp = *tpp = table(tp, level); + NEW0(p, arena); + p->sym.name = (char *)name; + p->sym.scope = level; + p->sym.up = tp->all; + tp->all = &p->sym; + p->link = tp->buckets[h]; + tp->buckets[h] = p; + return &p->sym; +} +Symbol relocate(const char *name, Table src, Table dst) { + struct entry *p, **q; + Symbol *r; + unsigned h = (unsigned long)name&(HASHSIZE-1); + + for (q = &src->buckets[h]; *q; q = &(*q)->link) + if (name == (*q)->sym.name) + break; + assert(*q); + /* + Remove the entry from src's hash chain + and from its list of all symbols. + */ + p = *q; + *q = (*q)->link; + for (r = &src->all; *r && *r != &p->sym; r = &(*r)->up) + ; + assert(*r == &p->sym); + *r = p->sym.up; + /* + Insert the entry into dst's hash chain + and into its list of all symbols. + Return the symbol-table entry. + */ + p->link = dst->buckets[h]; + dst->buckets[h] = p; + p->sym.up = dst->all; + dst->all = &p->sym; + return &p->sym; +} +Symbol lookup(const char *name, Table tp) { + struct entry *p; + unsigned h = (unsigned long)name&(HASHSIZE-1); + + assert(tp); + do + for (p = tp->buckets[h]; p; p = p->link) + if (name == p->sym.name) + return &p->sym; + while ((tp = tp->previous) != NULL); + return NULL; +} +int genlabel(int n) { + static int label = 1; + + label += n; + return label - n; +} +Symbol findlabel(int lab) { + struct entry *p; + unsigned h = lab&(HASHSIZE-1); + + for (p = labels->buckets[h]; p; p = p->link) + if (lab == p->sym.u.l.label) + return &p->sym; + NEW0(p, FUNC); + p->sym.name = stringd(lab); + p->sym.scope = LABELS; + p->sym.up = labels->all; + labels->all = &p->sym; + p->link = labels->buckets[h]; + labels->buckets[h] = p; + p->sym.generated = 1; + p->sym.u.l.label = lab; + (*IR->defsymbol)(&p->sym); + return &p->sym; +} +Symbol constant(Type ty, Value v) { + struct entry *p; + unsigned h = v.u&(HASHSIZE-1); + + ty = unqual(ty); + for (p = constants->buckets[h]; p; p = p->link) + if (eqtype(ty, p->sym.type, 1)) + switch (ty->op) { + case INT: if (equalp(i)) return &p->sym; break; + case UNSIGNED: if (equalp(u)) return &p->sym; break; + case FLOAT: if (equalp(d)) return &p->sym; break; + case FUNCTION: if (equalp(g)) return &p->sym; break; + case ARRAY: + case POINTER: if (equalp(p)) return &p->sym; break; + default: assert(0); + } + NEW0(p, PERM); + p->sym.name = vtoa(ty, v); + p->sym.scope = CONSTANTS; + p->sym.type = ty; + p->sym.sclass = STATIC; + p->sym.u.c.v = v; + p->link = constants->buckets[h]; + p->sym.up = constants->all; + constants->all = &p->sym; + constants->buckets[h] = p; + if (ty->u.sym && !ty->u.sym->addressed) + (*IR->defsymbol)(&p->sym); + p->sym.defined = 1; + return &p->sym; +} +Symbol intconst(int n) { + Value v; + + v.i = n; + return constant(inttype, v); +} +Symbol genident(int scls, Type ty, int lev) { + Symbol p; + + NEW0(p, lev >= LOCAL ? FUNC : PERM); + p->name = stringd(genlabel(1)); + p->scope = lev; + p->sclass = scls; + p->type = ty; + p->generated = 1; + if (lev == GLOBAL) + (*IR->defsymbol)(p); + return p; +} + +Symbol temporary(int scls, Type ty) { + Symbol p; + + NEW0(p, FUNC); + p->name = stringd(++tempid); + p->scope = level < LOCAL ? LOCAL : level; + p->sclass = scls; + p->type = ty; + p->temporary = 1; + p->generated = 1; + return p; +} +Symbol newtemp(int sclass, int tc, int size) { + Symbol p = temporary(sclass, btot(tc, size)); + + (*IR->local)(p); + p->defined = 1; + return p; +} + +Symbol allsymbols(Table tp) { + return tp->all; +} + +void locus(Table tp, Coordinate *cp) { + loci = append(cp, loci); + symbols = append(allsymbols(tp), symbols); +} + +void use(Symbol p, Coordinate src) { + Coordinate *cp; + + NEW(cp, PERM); + *cp = src; + p->uses = append(cp, p->uses); +} +/* findtype - find type ty in identifiers */ +Symbol findtype(Type ty) { + Table tp = identifiers; + int i; + struct entry *p; + + assert(tp); + do + for (i = 0; i < HASHSIZE; i++) + for (p = tp->buckets[i]; p; p = p->link) + if (p->sym.type == ty && p->sym.sclass == TYPEDEF) + return &p->sym; + while ((tp = tp->previous) != NULL); + return NULL; +} + +/* mkstr - make a string constant */ +Symbol mkstr(char *str) { + Value v; + Symbol p; + + v.p = str; + p = constant(array(chartype, strlen(v.p) + 1, 0), v); + if (p->u.c.loc == NULL) + p->u.c.loc = genident(STATIC, p->type, GLOBAL); + return p; +} + +/* mksymbol - make a symbol for name, install in &globals if sclass==EXTERN */ +Symbol mksymbol(int sclass, const char *name, Type ty) { + Symbol p; + + if (sclass == EXTERN) + p = install(string(name), &globals, GLOBAL, PERM); + else { + NEW0(p, PERM); + p->name = string(name); + p->scope = GLOBAL; + } + p->sclass = sclass; + p->type = ty; + (*IR->defsymbol)(p); + p->defined = 1; + return p; +} + +/* vtoa - return string for the constant v of type ty */ +char *vtoa(Type ty, Value v) { + + ty = unqual(ty); + switch (ty->op) { + case INT: return stringd(v.i); + case UNSIGNED: return stringf((v.u&~0x7FFF) ? "0x%X" : "%U", v.u); + case FLOAT: return stringf("%g", (double)v.d); + case ARRAY: + if (ty->type == chartype || ty->type == signedchar + || ty->type == unsignedchar) + return v.p; + return stringf("%p", v.p); + case POINTER: return stringf("%p", v.p); + case FUNCTION: return stringf("%p", v.g); + } + assert(0); return NULL; +} diff --git a/src/tools/lcc/src/symbolic.c b/src/tools/lcc/src/symbolic.c new file mode 100644 index 0000000..affa67a --- /dev/null +++ b/src/tools/lcc/src/symbolic.c @@ -0,0 +1,494 @@ +#include <time.h> +#include <ctype.h> +#include "c.h" + +#define I(f) s_##f + +static Node *tail; +static int off, maxoff, uid = 0, verbose = 0, html = 0; + +static const char *yyBEGIN(const char *tag) { + if (html) + print("<%s>", tag); + return tag; +} + +static void yyEND(const char *tag) { + if (html) + print("</%s>", tag); + if (isupper(*tag)) + print("\n"); +} + +#define BEGIN(tag) do { const char *yytag=yyBEGIN(#tag); +#define END yyEND(yytag); } while (0) +#define ITEM BEGIN(li) +#define START BEGIN(LI) +#define ANCHOR(attr,code) do { const char *yytag="a"; if (html) { printf("<a " #attr "=\""); code; print("\">"); } +#define NEWLINE print(html ? "<br>\n" : "\n") + +static void emitCoord(Coordinate src) { + if (src.file && *src.file) { + ANCHOR(href,print("%s", src.file)); print("%s", src.file); END; + print(":"); + } + print("%d.%d", src.y, src.x); +} + +static void emitString(int len, const char *s) { + for ( ; len-- > 0; s++) + if (*s == '&' && html) + print("&"); + else if (*s == '<' && html) + print("<"); + else if (*s == '>' && html) + print("<"); + else if (*s == '"' || *s == '\\') + print("\\%c", *s); + else if (*s >= ' ' && *s < 0177) + print("%c", *s); + else + print("\\%d%d%d", (*s>>6)&3, (*s>>3)&7, *s&7); +} + +static void emitSymRef(Symbol p) { + (*IR->defsymbol)(p); + ANCHOR(href,print("#%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END; +} + +static void emitSymbol(Symbol p) { + (*IR->defsymbol)(p); + ANCHOR(name,print("%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END; + BEGIN(ul); +#define xx(field,code) ITEM; if (!html) print(" "); print(#field "="); code; END + if (verbose && (src.y || src.x)) + xx(src,emitCoord(p->src)); + xx(type,print("%t", p->type)); + xx(sclass,print("%k", p->sclass)); + switch (p->scope) { + case CONSTANTS: xx(scope,print("CONSTANTS")); break; + case LABELS: xx(scope,print("LABELS")); break; + case GLOBAL: xx(scope,print("GLOBAL")); break; + case PARAM: xx(scope,print("PARAM")); break; + case LOCAL: xx(scope,print("LOCAL")); break; + default: + if (p->scope > LOCAL) + xx(scope,print("LOCAL+%d", p->scope-LOCAL)); + else + xx(scope,print("%d", p->scope)); + } + if (p->scope >= PARAM && p->sclass != STATIC) + xx(offset,print("%d", p->x.offset)); + xx(ref,print("%f", p->ref)); + if (p->temporary && p->u.t.cse) + xx(u.t.cse,print("%p", p->u.t.cse)); + END; +#undef xx +} + +/* address - initialize q for addressing expression p+n */ +static void I(address)(Symbol q, Symbol p, long n) { + q->name = stringf("%s%s%D", p->name, n > 0 ? "+" : "", n); + (*IR->defsymbol)(q); + START; print("address "); emitSymbol(q); END; +} + +/* blockbeg - start a block */ +static void I(blockbeg)(Env *e) { + e->offset = off; + START; print("blockbeg off=%d", off); END; +} + +/* blockend - start a block */ +static void I(blockend)(Env *e) { + if (off > maxoff) + maxoff = off; + START; print("blockend off=%d", off); END; + off = e->offset; +} + +/* defaddress - initialize an address */ +static void I(defaddress)(Symbol p){ + START; print("defaddress "); emitSymRef(p); END; +} + +/* defconst - define a constant */ +static void I(defconst)(int suffix, int size, Value v) { + START; + print("defconst "); + switch (suffix) { + case I: + print("int.%d ", size); + BEGIN(code); + if (size > sizeof (int)) + print("%D", v.i); + else + print("%d", (int)v.i); + END; + break; + case U: + print("unsigned.%d ", size); + BEGIN(code); + if (size > sizeof (unsigned)) + print("%U", v.u); + else + print("%u", (unsigned)v.u); + END; + break; + case P: print("void*.%d ", size); BEGIN(code); print("%p", v.p); END; break; + case F: print("float.%d ", size); BEGIN(code); print("%g", (double)v.d); END; break; + default: assert(0); + } + END; +} + +/* defstring - emit a string constant */ +static void I(defstring)(int len, char *s) { + START; print("defstring "); + BEGIN(code); print("\""); emitString(len, s); print("\""); END; + END; +} + +/* defsymbol - define a symbol: initialize p->x */ +static void I(defsymbol)(Symbol p) { + if (p->x.name == NULL) + p->x.name = stringd(++uid); +} + +/* emit - emit the dags on list p */ +static void I(emit)(Node p){ + ITEM; + if (!html) + print(" "); + for (; p; p = p->x.next) { + if (p->op == LABEL+V) { + assert(p->syms[0]); + ANCHOR(name,print("%s", p->syms[0]->x.name)); + BEGIN(code); print("%s", p->syms[0]->name); END; + END; + print(":"); + } else { + int i; + if (p->x.listed) { + BEGIN(strong); print("%d", p->x.inst); END; print("'"); + print(" %s", opname(p->op)); + } else + print("%d. %s", p->x.inst, opname(p->op)); + if (p->count > 1) + print(" count=%d", p->count); + for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++) + print(" #%d", p->kids[i]->x.inst); + if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type) + print(" {%t}", p->syms[0]->type); + else + for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++) { + print(" "); + if (p->syms[i]->scope == CONSTANTS) + print(p->syms[i]->name); + else + emitSymRef(p->syms[i]); + } + } + NEWLINE; + } + END; +} + +/* export - announce p as exported */ +static void I(export)(Symbol p) { + START; print("export "); emitSymRef(p); END; +} + +/* function - generate code for a function */ +static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) { + int i; + + (*IR->defsymbol)(f); + off = 0; + for (i = 0; caller[i] && callee[i]; i++) { + off = roundup(off, caller[i]->type->align); + caller[i]->x.offset = callee[i]->x.offset = off; + off += caller[i]->type->size; + } + if (!html) { + print("function "); + emitSymbol(f); + print(" ncalls=%d\n", ncalls); + for (i = 0; caller[i]; i++) + START; print("caller "); emitSymbol(caller[i]); END; + for (i = 0; callee[i]; i++) + START; print("callee "); emitSymbol(callee[i]); END; + } else { + START; + print("function"); + BEGIN(UL); +#define xx(field,code) ITEM; print(#field "="); code; END + xx(f,emitSymbol(f)); + xx(ncalls,print("%d", ncalls)); + if (caller[0]) { + ITEM; print("caller"); BEGIN(OL); + for (i = 0; caller[i]; i++) + ITEM; emitSymbol(caller[i]); END; + END; END; + ITEM; print("callee"); BEGIN(OL); + for (i = 0; callee[i]; i++) + ITEM; emitSymbol(callee[i]); END; + END; END; + } else { + xx(caller,BEGIN(em); print("empty"); END); + xx(callee,BEGIN(em); print("empty"); END); + } + END; + END; + } + maxoff = off = 0; + gencode(caller, callee); + if (html) + START; print("emitcode"); BEGIN(ul); emitcode(); END; END; + else + emitcode(); + START; print("maxoff=%d", maxoff); END; +#undef xx +} + +/* visit - generate code for *p */ +static int visit(Node p, int n) { + if (p && p->x.inst == 0) { + p->x.inst = ++n; + n = visit(p->kids[0], n); + n = visit(p->kids[1], n); + *tail = p; + tail = &p->x.next; + } + return n; +} + +/* gen0 - generate code for the dags on list p */ +static Node I(gen)(Node p) { + int n; + Node nodelist; + + tail = &nodelist; + for (n = 0; p; p = p->link) { + switch (generic(p->op)) { /* check for valid forest */ + case CALL: + assert(IR->wants_dag || p->count == 0); + break; + case ARG: + case ASGN: case JUMP: case LABEL: case RET: + case EQ: case GE: case GT: case LE: case LT: case NE: + assert(p->count == 0); + break; + case INDIR: + assert(IR->wants_dag && p->count > 0); + break; + default: + assert(0); + } + check(p); + p->x.listed = 1; + n = visit(p, n); + } + *tail = 0; + return nodelist; +} + +/* global - announce a global */ +static void I(global)(Symbol p) { + START; print("global "); emitSymbol(p); END; +} + +/* import - import a symbol */ +static void I(import)(Symbol p) { + START; print("import "); emitSymRef(p); END; +} + +/* local - local variable */ +static void I(local)(Symbol p) { + if (p->temporary) + p->name = stringf("t%s", p->name); + (*IR->defsymbol)(p); + off = roundup(off, p->type->align); + p->x.offset = off; + off += p->type->size; + START; print(p->temporary ? "temporary " : "local "); emitSymbol(p); END; +} + +/* progbeg - beginning of program */ +static void I(progbeg)(int argc, char *argv[]) { + int i; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-v") == 0) + verbose++; + else if (strcmp(argv[i], "-html") == 0) + html++; + if (html) { + print("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"); + print("<html>"); + BEGIN(head); + if (firstfile && *firstfile) + BEGIN(title); emitString(strlen(firstfile), firstfile); END; + print("<link rev=made href=\"mailto:drh@microsoft.com\">\n"); + END; + print("<body>\n"); + if (firstfile && *firstfile) + BEGIN(h1); emitString(strlen(firstfile), firstfile); END; + BEGIN(P); BEGIN(em); + print("Links lead from uses of identifiers and labels to their definitions."); + END; END; + print("<ul>\n"); + START; + print("progbeg"); + BEGIN(ol); + for (i = 1; i < argc; i++) { + ITEM; + BEGIN(code); print("\""); emitString(strlen(argv[i]), argv[i]); print("\""); END; + END; + } + END; + END; + } +} + +/* progend - end of program */ +static void I(progend)(void) { + START; print("progend"); END; + if (html) { + time_t t; + print("</ul>\n"); + time(&t); + print("<hr><address>%s</address>\n", ctime(&t)); + print("</body></html>\n"); + } +} + +/* segment - switch to segment s */ +static void I(segment)(int s) { + START; print("segment %s", &"text\0bss\0.data\0lit\0.sym\0."[5*s-5]); END; +} + +/* space - initialize n bytes of space */ +static void I(space)(int n) { + START; print("space %d", n); END; +} + +static void I(stabblock)(int brace, int lev, Symbol *p) {} + +/* stabend - finalize stab output */ +static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) { + int i; + + if (p) + emitSymRef(p); + print("\n"); + if (cpp && sp) + for (i = 0; cpp[i] && sp[i]; i++) { + print("%w.%d: ", cpp[i], cpp[i]->x); + emitSymRef(sp[i]); + print("\n"); + } +} + +static void I(stabfend)(Symbol p, int lineno) {} +static void I(stabinit)(char *file, int argc, char *argv[]) {} + +/* stabline - emit line number information for source coordinate *cp */ +static void I(stabline)(Coordinate *cp) { + if (cp->file) + print("%s:", cp->file); + print("%d.%d:\n", cp->y, cp->x); +} + +static void I(stabsym)(Symbol p) {} +static void I(stabtype)(Symbol p) {} + +Interface symbolicIR = { + {1, 1, 0}, /* char */ + {2, 2, 0}, /* short */ + {4, 4, 0}, /* int */ + {4, 4, 0}, /* long */ + {4, 4, 0}, /* long long */ + {4, 4, 1}, /* float */ + {8, 8, 1}, /* double */ + {8, 8, 1}, /* long double */ + {4, 4, 0}, /* T* */ + {0, 4, 0}, /* struct */ + 0, /* little_endian */ + 0, /* mulops_calls */ + 0, /* wants_callb */ + 1, /* wants_argb */ + 1, /* left_to_right */ + 1, /* wants_dag */ + 0, /* unsigned_char */ + I(address), + I(blockbeg), + I(blockend), + I(defaddress), + I(defconst), + I(defstring), + I(defsymbol), + I(emit), + I(export), + I(function), + I(gen), + I(global), + I(import), + I(local), + I(progbeg), + I(progend), + I(segment), + I(space), + I(stabblock), + I(stabend), + I(stabfend), + I(stabinit), + I(stabline), + I(stabsym), + I(stabtype) +}; + +Interface symbolic64IR = { + {1, 1, 0}, /* char */ + {2, 2, 0}, /* short */ + {4, 4, 0}, /* int */ + {8, 8, 0}, /* long */ + {8, 8, 0}, /* long long */ + {4, 4, 1}, /* float */ + {8, 8, 1}, /* double */ + {8, 8, 1}, /* long double */ + {8, 8, 0}, /* T* */ + {0, 1, 0}, /* struct */ + 1, /* little_endian */ + 0, /* mulops_calls */ + 0, /* wants_callb */ + 1, /* wants_argb */ + 1, /* left_to_right */ + 1, /* wants_dag */ + 0, /* unsigned_char */ + I(address), + I(blockbeg), + I(blockend), + I(defaddress), + I(defconst), + I(defstring), + I(defsymbol), + I(emit), + I(export), + I(function), + I(gen), + I(global), + I(import), + I(local), + I(progbeg), + I(progend), + I(segment), + I(space), + I(stabblock), + I(stabend), + I(stabfend), + I(stabinit), + I(stabline), + I(stabsym), + I(stabtype) +}; diff --git a/src/tools/lcc/src/token.h b/src/tools/lcc/src/token.h new file mode 100644 index 0000000..d309f9b --- /dev/null +++ b/src/tools/lcc/src/token.h @@ -0,0 +1,133 @@ +/* +xx(symbol, value, prec, op, optree, kind, string) +*/ +yy(0, 0, 0, 0, 0, 0, 0) +xx(FLOAT, 1, 0, 0, 0, CHAR, "float") +xx(DOUBLE, 2, 0, 0, 0, CHAR, "double") +xx(CHAR, 3, 0, 0, 0, CHAR, "char") +xx(SHORT, 4, 0, 0, 0, CHAR, "short") +xx(INT, 5, 0, 0, 0, CHAR, "int") +xx(UNSIGNED, 6, 0, 0, 0, CHAR, "unsigned") +xx(POINTER, 7, 0, 0, 0, 0, "pointer") +xx(VOID, 8, 0, 0, 0, CHAR, "void") +xx(STRUCT, 9, 0, 0, 0, CHAR, "struct") +xx(UNION, 10, 0, 0, 0, CHAR, "union") +xx(FUNCTION, 11, 0, 0, 0, 0, "function") +xx(ARRAY, 12, 0, 0, 0, 0, "array") +xx(ENUM, 13, 0, 0, 0, CHAR, "enum") +xx(LONG, 14, 0, 0, 0, CHAR, "long") +xx(CONST, 15, 0, 0, 0, CHAR, "const") +xx(VOLATILE, 16, 0, 0, 0, CHAR, "volatile") +yy(0, 17, 0, 0, 0, 0, 0) +yy(0, 18, 0, 0, 0, 0, 0) +yy(0, 19, 0, 0, 0, 0, 0) +yy(0, 20, 0, 0, 0, 0, 0) +yy(0, 21, 0, 0, 0, 0, 0) +yy(0, 22, 0, 0, 0, 0, 0) +yy(0, 23, 0, 0, 0, 0, 0) +yy(0, 24, 0, 0, 0, 0, 0) +yy(0, 25, 0, 0, 0, 0, 0) +yy(0, 26, 0, 0, 0, 0, 0) +yy(0, 27, 0, 0, 0, 0, 0) +yy(0, 28, 0, 0, 0, 0, "long long") +yy(0, 29, 0, 0, 0, 0, 0) +yy(0, 30, 0, 0, 0, 0, 0) +yy(0, 31, 0, 0, 0, 0, "const volatile") +xx(ID, 32, 0, 0, 0, ID, "identifier") +yy(0, 33, 0, 0, 0, ID, "!") +xx(FCON, 34, 0, 0, 0, ID, "floating constant") +xx(ICON, 35, 0, 0, 0, ID, "integer constant") +xx(SCON, 36, 0, 0, 0, ID, "string constant") +yy(0, 37, 13, MOD, bittree,'%', "%") +yy(0, 38, 8, BAND, bittree,ID, "&") +xx(INCR, 39, 0, ADD, addtree,ID, "++") +yy(0, 40, 0, 0, 0, ID, "(") +yy(0, 41, 0, 0, 0, ')', ")") +yy(0, 42, 13, MUL, multree,ID, "*") +yy(0, 43, 12, ADD, addtree,ID, "+") +yy(0, 44, 1, 0, 0, ',', ",") +yy(0, 45, 12, SUB, subtree,ID, "-") +yy(0, 46, 0, 0, 0, '.', ".") +yy(0, 47, 13, DIV, multree,'/', "/") +xx(DECR, 48, 0, SUB, subtree,ID, "--") +xx(DEREF, 49, 0, 0, 0, DEREF, "->") +xx(ANDAND, 50, 5, AND, andtree,ANDAND, "&&") +xx(OROR, 51, 4, OR, andtree,OROR, "||") +xx(LEQ, 52, 10, LE, cmptree,LEQ, "<=") +xx(EQL, 53, 9, EQ, eqtree, EQL, "==") +xx(NEQ, 54, 9, NE, eqtree, NEQ, "!=") +xx(GEQ, 55, 10, GE, cmptree,GEQ, ">=") +xx(RSHIFT, 56, 11, RSH, shtree, RSHIFT, ">>") +xx(LSHIFT, 57, 11, LSH, shtree, LSHIFT, "<<") +yy(0, 58, 0, 0, 0, ':', ":") +yy(0, 59, 0, 0, 0, IF, ";") +yy(0, 60, 10, LT, cmptree,'<', "<") +yy(0, 61, 2, ASGN, asgntree,'=', "=") +yy(0, 62, 10, GT, cmptree,'>', ">") +yy(0, 63, 0, 0, 0, '?', "?") +xx(ELLIPSIS, 64, 0, 0, 0, ELLIPSIS,"...") +xx(SIZEOF, 65, 0, 0, 0, ID, "sizeof") +yy(0, 66, 0, 0, 0, 0, 0) +xx(AUTO, 67, 0, 0, 0, STATIC, "auto") +xx(BREAK, 68, 0, 0, 0, IF, "break") +xx(CASE, 69, 0, 0, 0, IF, "case") +xx(CONTINUE, 70, 0, 0, 0, IF, "continue") +xx(DEFAULT, 71, 0, 0, 0, IF, "default") +xx(DO, 72, 0, 0, 0, IF, "do") +xx(ELSE, 73, 0, 0, 0, IF, "else") +xx(EXTERN, 74, 0, 0, 0, STATIC, "extern") +xx(FOR, 75, 0, 0, 0, IF, "for") +xx(GOTO, 76, 0, 0, 0, IF, "goto") +xx(IF, 77, 0, 0, 0, IF, "if") +xx(REGISTER, 78, 0, 0, 0, STATIC, "register") +xx(RETURN, 79, 0, 0, 0, IF, "return") +xx(SIGNED, 80, 0, 0, 0, CHAR, "signed") +xx(STATIC, 81, 0, 0, 0, STATIC, "static") +xx(SWITCH, 82, 0, 0, 0, IF, "switch") +xx(TYPEDEF, 83, 0, 0, 0, STATIC, "typedef") +xx(WHILE, 84, 0, 0, 0, IF, "while") +xx(TYPECODE, 85, 0, 0, 0, ID, "__typecode") +xx(FIRSTARG, 86, 0, 0, 0, ID, "__firstarg") +yy(0, 87, 0, 0, 0, 0, 0) +yy(0, 88, 0, 0, 0, 0, 0) +yy(0, 89, 0, 0, 0, 0, 0) +yy(0, 90, 0, 0, 0, 0, 0) +yy(0, 91, 0, 0, 0, '[', "[") +yy(0, 92, 0, 0, 0, 0, 0) +yy(0, 93, 0, 0, 0, ']', "]") +yy(0, 94, 7, BXOR, bittree,'^', "^") +yy(0, 95, 0, 0, 0, 0, 0) +yy(0, 96, 0, 0, 0, 0, 0) +yy(0, 97, 0, 0, 0, 0, 0) +yy(0, 98, 0, 0, 0, 0, 0) +yy(0, 99, 0, 0, 0, 0, 0) +yy(0, 100, 0, 0, 0, 0, 0) +yy(0, 101, 0, 0, 0, 0, 0) +yy(0, 102, 0, 0, 0, 0, 0) +yy(0, 103, 0, 0, 0, 0, 0) +yy(0, 104, 0, 0, 0, 0, 0) +yy(0, 105, 0, 0, 0, 0, 0) +yy(0, 106, 0, 0, 0, 0, 0) +yy(0, 107, 0, 0, 0, 0, 0) +yy(0, 108, 0, 0, 0, 0, 0) +yy(0, 109, 0, 0, 0, 0, 0) +yy(0, 110, 0, 0, 0, 0, 0) +yy(0, 111, 0, 0, 0, 0, 0) +yy(0, 112, 0, 0, 0, 0, 0) +yy(0, 113, 0, 0, 0, 0, 0) +yy(0, 114, 0, 0, 0, 0, 0) +yy(0, 115, 0, 0, 0, 0, 0) +yy(0, 116, 0, 0, 0, 0, 0) +yy(0, 117, 0, 0, 0, 0, 0) +yy(0, 118, 0, 0, 0, 0, 0) +yy(0, 119, 0, 0, 0, 0, 0) +yy(0, 120, 0, 0, 0, 0, 0) +yy(0, 121, 0, 0, 0, 0, 0) +yy(0, 122, 0, 0, 0, 0, 0) +yy(0, 123, 0, 0, 0, IF, "{") +yy(0, 124, 6, BOR, bittree,'|', "|") +yy(0, 125, 0, 0, 0, '}', "}") +yy(0, 126, 0, BCOM, 0, ID, "~") +xx(EOI, 127, 0, 0, 0, EOI, "end of input") +#undef xx +#undef yy diff --git a/src/tools/lcc/src/trace.c b/src/tools/lcc/src/trace.c new file mode 100644 index 0000000..3b9ba78 --- /dev/null +++ b/src/tools/lcc/src/trace.c @@ -0,0 +1,181 @@ +#include "c.h" + + +static char *fmt, *fp, *fmtend; /* format string, current & limit pointer */ +static Tree args; /* printf arguments */ +static Symbol frameno; /* local holding frame number */ + +/* appendstr - append str to the evolving format string, expanding it if necessary */ +static void appendstr(char *str) { + do + if (fp == fmtend) { + if (fp) { + char *s = allocate(2*(fmtend - fmt), FUNC); + strncpy(s, fmt, fmtend - fmt); + fp = s + (fmtend - fmt); + fmtend = s + 2*(fmtend - fmt); + fmt = s; + } else { + fp = fmt = allocate(80, FUNC); + fmtend = fmt + 80; + } + } + while ((*fp++ = *str++) != 0); + fp--; +} + +/* tracevalue - append format and argument to print the value of e */ +static void tracevalue(Tree e, int lev) { + Type ty = unqual(e->type); + + switch (ty->op) { + case INT: + if (ty == chartype || ty == signedchar) + appendstr("'\\x%02x'"); + else if (ty == longtype) + appendstr("0x%ld"); + else + appendstr("0x%d"); + break; + case UNSIGNED: + if (ty == chartype || ty == unsignedchar) + appendstr("'\\x%02x'"); + else if (ty == unsignedlong) + appendstr("0x%lx"); + else + appendstr("0x%x"); + break; + case FLOAT: + if (ty == longdouble) + appendstr("%Lg"); + else + appendstr("%g"); + break; + case POINTER: + if (unqual(ty->type) == chartype + || unqual(ty->type) == signedchar + || unqual(ty->type) == unsignedchar) { + static Symbol null; + if (null == NULL) + null = mkstr("(null)"); + tracevalue(cast(e, unsignedtype), lev + 1); + appendstr(" \"%.30s\""); + e = condtree(e, e, pointer(idtree(null->u.c.loc))); + } else { + appendstr("("); appendstr(typestring(ty, "")); appendstr(")0x%x"); + } + break; + case STRUCT: { + Field q; + appendstr("("); appendstr(typestring(ty, "")); appendstr("){"); + for (q = ty->u.sym->u.s.flist; q; q = q->link) { + appendstr(q->name); appendstr("="); + tracevalue(field(addrof(e), q->name), lev + 1); + if (q->link) + appendstr(","); + } + appendstr("}"); + return; + } + case UNION: + appendstr("("); appendstr(typestring(ty, "")); appendstr("){...}"); + return; + case ARRAY: + if (lev && ty->type->size > 0) { + int i; + e = pointer(e); + appendstr("{"); + for (i = 0; i < ty->size/ty->type->size; i++) { + Tree p = (*optree['+'])(ADD, e, consttree(i, inttype)); + if (isptr(p->type) && isarray(p->type->type)) + p = retype(p, p->type->type); + else + p = rvalue(p); + if (i) + appendstr(","); + tracevalue(p, lev + 1); + } + appendstr("}"); + } else + appendstr(typestring(ty, "")); + return; + default: + assert(0); + } + e = cast(e, promote(ty)); + args = tree(mkop(ARG,e->type), e->type, e, args); +} + +/* tracefinis - complete & generate the trace call to print */ +static void tracefinis(Symbol printer) { + Tree *ap; + Symbol p; + + *fp = 0; + p = mkstr(string(fmt)); + for (ap = &args; *ap; ap = &(*ap)->kids[1]) + ; + *ap = tree(ARG+P, charptype, pointer(idtree(p->u.c.loc)), 0); + walk(calltree(pointer(idtree(printer)), freturn(printer->type), args, NULL), 0, 0); + args = 0; + fp = fmtend = 0; +} + +/* tracecall - generate code to trace entry to f */ +static void tracecall(Symbol printer, Symbol f) { + int i; + Symbol counter = genident(STATIC, inttype, GLOBAL); + + defglobal(counter, BSS); + (*IR->space)(counter->type->size); + frameno = genident(AUTO, inttype, level); + addlocal(frameno); + appendstr(f->name); appendstr("#"); + tracevalue(asgn(frameno, incr(INCR, idtree(counter), consttree(1, inttype))), 0); + appendstr("("); + for (i = 0; f->u.f.callee[i]; i++) { + if (i) + appendstr(","); + appendstr(f->u.f.callee[i]->name); appendstr("="); + tracevalue(idtree(f->u.f.callee[i]), 0); + } + if (variadic(f->type)) + appendstr(",..."); + appendstr(") called\n"); + tracefinis(printer); +} + +/* tracereturn - generate code to trace return e */ +static void tracereturn(Symbol printer, Symbol f, Tree e) { + appendstr(f->name); appendstr("#"); + tracevalue(idtree(frameno), 0); + appendstr(" returned"); + if (freturn(f->type) != voidtype && e) { + appendstr(" "); + tracevalue(e, 0); + } + appendstr("\n"); + tracefinis(printer); +} + +/* trace_init - initialize for tracing */ +void trace_init(int argc, char *argv[]) { + int i; + static int inited; + + if (inited) + return; + inited = 1; + type_init(argc, argv); + if (IR) + for (i = 1; i < argc; i++) + if (strncmp(argv[i], "-t", 2) == 0 && strchr(argv[i], '=') == NULL) { + Symbol printer = mksymbol(EXTERN, + argv[i][2] ? &argv[i][2] : "printf", + ftype(inttype, ptr(qual(CONST, chartype)))); + printer->defined = 0; + attach((Apply)tracecall, printer, &events.entry); + attach((Apply)tracereturn, printer, &events.returns); + break; + } +} diff --git a/src/tools/lcc/src/tree.c b/src/tools/lcc/src/tree.c new file mode 100644 index 0000000..d2b6a91 --- /dev/null +++ b/src/tools/lcc/src/tree.c @@ -0,0 +1,223 @@ +#include "c.h" + + +int where = STMT; +static int warn; +static int nid = 1; /* identifies trees & nodes in debugging output */ +static struct nodeid { + int printed; + Tree node; +} ids[500]; /* if ids[i].node == p, then p's id is i */ + +static void printtree1(Tree, int, int); + +Tree tree(int op, Type type, Tree left, Tree right) { + Tree p; + + NEW0(p, where); + p->op = op; + p->type = type; + p->kids[0] = left; + p->kids[1] = right; + return p; +} + +Tree texpr(Tree (*f)(int), int tok, int a) { + int save = where; + Tree p; + + where = a; + p = (*f)(tok); + where = save; + return p; +} +static Tree root1(Tree p) { + if (p == NULL) + return p; + if (p->type == voidtype) + warn++; + switch (generic(p->op)) { + case COND: { + Tree q = p->kids[1]; + assert(q && q->op == RIGHT); + if (p->u.sym && q->kids[0] && generic(q->kids[0]->op) == ASGN) + q->kids[0] = root1(q->kids[0]->kids[1]); + else + q->kids[0] = root1(q->kids[0]); + if (p->u.sym && q->kids[1] && generic(q->kids[1]->op) == ASGN) + q->kids[1] = root1(q->kids[1]->kids[1]); + else + q->kids[1] = root1(q->kids[1]); + p->u.sym = 0; + if (q->kids[0] == 0 && q->kids[1] == 0) + p = root1(p->kids[0]); + } + break; + case AND: case OR: + if ((p->kids[1] = root1(p->kids[1])) == 0) + p = root1(p->kids[0]); + break; + case NOT: + if (warn++ == 0) + warning("expression with no effect elided\n"); + return root1(p->kids[0]); + case RIGHT: + if (p->kids[1] == 0) + return root1(p->kids[0]); + if (p->kids[0] && p->kids[0]->op == CALL+B + && p->kids[1] && p->kids[1]->op == INDIR+B) + /* avoid premature release of the CALL+B temporary */ + return p->kids[0]; + if (p->kids[0] && p->kids[0]->op == RIGHT + && p->kids[1] == p->kids[0]->kids[0]) + /* de-construct e++ construction */ + return p->kids[0]->kids[1]; + p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1])); + return p->kids[0] || p->kids[1] ? p : (Tree)0; + case EQ: case NE: case GT: case GE: case LE: case LT: + case ADD: case SUB: case MUL: case DIV: case MOD: + case LSH: case RSH: case BAND: case BOR: case BXOR: + if (warn++ == 0) + warning("expression with no effect elided\n"); + p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1])); + return p->kids[0] || p->kids[1] ? p : (Tree)0; + case INDIR: + if (p->type->size == 0 && unqual(p->type) != voidtype) + warning("reference to `%t' elided\n", p->type); + if (isptr(p->kids[0]->type) && isvolatile(p->kids[0]->type->type)) + warning("reference to `volatile %t' elided\n", p->type); + /* fall thru */ + case CVI: case CVF: case CVU: case CVP: + case NEG: case BCOM: case FIELD: + if (warn++ == 0) + warning("expression with no effect elided\n"); + return root1(p->kids[0]); + case ADDRL: case ADDRG: case ADDRF: case CNST: + if (needconst) + return p; + if (warn++ == 0) + warning("expression with no effect elided\n"); + return NULL; + case ARG: case ASGN: case CALL: case JUMP: case LABEL: + break; + default: assert(0); + } + return p; +} + +Tree root(Tree p) { + warn = 0; + return root1(p); +} + +char *opname(int op) { + static char *opnames[] = { + "", + "CNST", + "ARG", + "ASGN", + "INDIR", + "CVC", + "CVD", + "CVF", + "CVI", + "CVP", + "CVS", + "CVU", + "NEG", + "CALL", + "*LOAD*", + "RET", + "ADDRG", + "ADDRF", + "ADDRL", + "ADD", + "SUB", + "LSH", + "MOD", + "RSH", + "BAND", + "BCOM", + "BOR", + "BXOR", + "DIV", + "MUL", + "EQ", + "GE", + "GT", + "LE", + "LT", + "NE", + "JUMP", + "LABEL", + "AND", + "NOT", + "OR", + "COND", + "RIGHT", + "FIELD" + }, *suffixes[] = { + "0", "F", "D", "C", "S", "I", "U", "P", "V", "B", + "10","11","12","13","14","15" + }; + + if (generic(op) >= AND && generic(op) <= FIELD && opsize(op) == 0) + return opnames[opindex(op)]; + return stringf("%s%s%s", + opindex(op) > 0 && opindex(op) < NELEMS(opnames) ? + opnames[opindex(op)] : stringd(opindex(op)), + suffixes[optype(op)], opsize(op) > 0 ? stringd(opsize(op)) : ""); +} + +int nodeid(Tree p) { + int i = 1; + + ids[nid].node = p; + while (ids[i].node != p) + i++; + if (i == nid) + ids[nid++].printed = 0; + return i; +} + +/* printed - return pointer to ids[id].printed */ +int *printed(int id) { + if (id) + return &ids[id].printed; + nid = 1; + return 0; +} + +/* printtree - print tree p on fd */ +void printtree(Tree p, int fd) { + (void)printed(0); + printtree1(p, fd, 1); +} + +/* printtree1 - recursively print tree p */ +static void printtree1(Tree p, int fd, int lev) { + FILE *f = fd == 1 ? stdout : stderr; + int i; + static char blanks[] = " "; + + if (p == 0 || *printed(i = nodeid(p))) + return; + fprint(f, "#%d%S%S", i, blanks, i < 10 ? 2 : i < 100 ? 1 : 0, blanks, lev); + fprint(f, "%s %t", opname(p->op), p->type); + *printed(i) = 1; + for (i = 0; i < NELEMS(p->kids); i++) + if (p->kids[i]) + fprint(f, " #%d", nodeid(p->kids[i])); + if (p->op == FIELD && p->u.field) + fprint(f, " %s %d..%d", p->u.field->name, + fieldsize(p->u.field) + fieldright(p->u.field), fieldright(p->u.field)); + else if (generic(p->op) == CNST) + fprint(f, " %s", vtoa(p->type, p->u.v)); + else if (p->u.sym) + fprint(f, " %s", p->u.sym->name); + if (p->node) + fprint(f, " node=%p", p->node); + fprint(f, "\n"); + for (i = 0; i < NELEMS(p->kids); i++) + printtree1(p->kids[i], fd, lev + 1); +} diff --git a/src/tools/lcc/src/types.c b/src/tools/lcc/src/types.c new file mode 100644 index 0000000..4aa3d18 --- /dev/null +++ b/src/tools/lcc/src/types.c @@ -0,0 +1,748 @@ +#include "c.h" +#include <float.h> + + +static Field isfield(const char *, Field); +static Type type(int, Type, int, int, void *); + +static struct entry { + struct type type; + struct entry *link; +} *typetable[128]; +static int maxlevel; + +static Symbol pointersym; + +Type chartype; /* char */ +Type doubletype; /* double */ +Type floattype; /* float */ +Type inttype; /* signed int */ +Type longdouble; /* long double */ +Type longtype; /* long */ +Type longlong; /* long long */ +Type shorttype; /* signed short int */ +Type signedchar; /* signed char */ +Type unsignedchar; /* unsigned char */ +Type unsignedlong; /* unsigned long int */ +Type unsignedlonglong; /* unsigned long long int */ +Type unsignedshort; /* unsigned short int */ +Type unsignedtype; /* unsigned int */ +Type funcptype; /* void (*)() */ +Type charptype; /* char* */ +Type voidptype; /* void* */ +Type voidtype; /* basic types: void */ +Type unsignedptr; /* unsigned type to hold void* */ +Type signedptr; /* signed type to hold void* */ +Type widechar; /* unsigned type that represents wchar_t */ + +static Type xxinit(int op, char *name, Metrics m) { + Symbol p = install(string(name), &types, GLOBAL, PERM); + Type ty = type(op, 0, m.size, m.align, p); + + assert(ty->align == 0 || ty->size%ty->align == 0); + p->type = ty; + p->addressed = m.outofline; + switch (ty->op) { + case INT: + p->u.limits.max.i = ones(8*ty->size)>>1; + p->u.limits.min.i = -p->u.limits.max.i - 1; + break; + case UNSIGNED: + p->u.limits.max.u = ones(8*ty->size); + p->u.limits.min.u = 0; + break; + case FLOAT: + if (ty->size == sizeof (float)) + p->u.limits.max.d = FLT_MAX; + else if (ty->size == sizeof (double)) + p->u.limits.max.d = DBL_MAX; + else + p->u.limits.max.d = LDBL_MAX; + p->u.limits.min.d = -p->u.limits.max.d; + break; + default: assert(0); + } + return ty; +} +static Type type(int op, Type ty, int size, int align, void *sym) { + unsigned h = (op^((unsigned long)ty>>3)) +&(NELEMS(typetable)-1); + struct entry *tn; + + if (op != FUNCTION && (op != ARRAY || size > 0)) + for (tn = typetable[h]; tn; tn = tn->link) + if (tn->type.op == op && tn->type.type == ty + && tn->type.size == size && tn->type.align == align + && tn->type.u.sym == sym) + return &tn->type; + NEW0(tn, PERM); + tn->type.op = op; + tn->type.type = ty; + tn->type.size = size; + tn->type.align = align; + tn->type.u.sym = sym; + tn->link = typetable[h]; + typetable[h] = tn; + return &tn->type; +} +void type_init(int argc, char *argv[]) { + static int inited; + int i; + + if (inited) + return; + inited = 1; + if (!IR) + return; + for (i = 1; i < argc; i++) { + int size, align, outofline; + if (strncmp(argv[i], "-unsigned_char=", 15) == 0) + IR->unsigned_char = argv[i][15] - '0'; +#define xx(name) \ + else if (sscanf(argv[i], "-" #name "=%d,%d,%d", &size, &align, &outofline) == 3) { \ + IR->name.size = size; IR->name.align = align; \ + IR->name.outofline = outofline; } + xx(charmetric) + xx(shortmetric) + xx(intmetric) + xx(longmetric) + xx(longlongmetric) + xx(floatmetric) + xx(doublemetric) + xx(longdoublemetric) + xx(ptrmetric) + xx(structmetric) +#undef xx + } +#define xx(v,name,op,metrics) v=xxinit(op,name,IR->metrics) + xx(chartype, "char", IR->unsigned_char ? UNSIGNED : INT,charmetric); + xx(doubletype, "double", FLOAT, doublemetric); + xx(floattype, "float", FLOAT, floatmetric); + xx(inttype, "int", INT, intmetric); + xx(longdouble, "long double", FLOAT, longdoublemetric); + xx(longtype, "long int", INT, longmetric); + xx(longlong, "long long int", INT, longlongmetric); + xx(shorttype, "short", INT, shortmetric); + xx(signedchar, "signed char", INT, charmetric); + xx(unsignedchar, "unsigned char", UNSIGNED,charmetric); + xx(unsignedlong, "unsigned long", UNSIGNED,longmetric); + xx(unsignedshort, "unsigned short", UNSIGNED,shortmetric); + xx(unsignedtype, "unsigned int", UNSIGNED,intmetric); + xx(unsignedlonglong,"unsigned long long",UNSIGNED,longlongmetric); +#undef xx + { + Symbol p; + p = install(string("void"), &types, GLOBAL, PERM); + voidtype = type(VOID, NULL, 0, 0, p); + p->type = voidtype; + } + pointersym = install(string("T*"), &types, GLOBAL, PERM); + pointersym->addressed = IR->ptrmetric.outofline; + pointersym->u.limits.max.p = (void*)ones(8*IR->ptrmetric.size); + pointersym->u.limits.min.p = 0; + voidptype = ptr(voidtype); + funcptype = ptr(func(voidtype, NULL, 1)); + charptype = ptr(chartype); +#define xx(v,t) if (v==NULL && t->size==voidptype->size && t->align==voidptype->align) v=t + xx(unsignedptr,unsignedshort); + xx(unsignedptr,unsignedtype); + xx(unsignedptr,unsignedlong); + xx(unsignedptr,unsignedlonglong); + if (unsignedptr == NULL) + unsignedptr = type(UNSIGNED, NULL, voidptype->size, voidptype->align, voidptype->u.sym); + xx(signedptr,shorttype); + xx(signedptr,inttype); + xx(signedptr,longtype); + xx(signedptr,longlong); + if (signedptr == NULL) + signedptr = type(INT, NULL, voidptype->size, voidptype->align, voidptype->u.sym); +#undef xx + widechar = unsignedshort; + for (i = 0; i < argc; i++) { +#define xx(name,type) \ + if (strcmp(argv[i], "-wchar_t=" #name) == 0) \ + widechar = type; + xx(unsigned_char,unsignedchar) + xx(unsigned_int,unsignedtype) + xx(unsigned_short,unsignedshort) + } +#undef xx +} +void rmtypes(int lev) { + if (maxlevel >= lev) { + int i; + maxlevel = 0; + for (i = 0; i < NELEMS(typetable); i++) { + struct entry *tn, **tq = &typetable[i]; + while ((tn = *tq) != NULL) + if (tn->type.op == FUNCTION) + tq = &tn->link; + else if (tn->type.u.sym && tn->type.u.sym->scope >= lev) + *tq = tn->link; + else { + if (tn->type.u.sym && tn->type.u.sym->scope > maxlevel) + maxlevel = tn->type.u.sym->scope; + tq = &tn->link; + } + + } + } +} +Type ptr(Type ty) { + return type(POINTER, ty, IR->ptrmetric.size, + IR->ptrmetric.align, pointersym); +} +Type deref(Type ty) { + if (isptr(ty)) + ty = ty->type; + else + error("type error: %s\n", "pointer expected"); + return isenum(ty) ? unqual(ty)->type : ty; +} +Type array(Type ty, int n, int a) { + assert(ty); + if (isfunc(ty)) { + error("illegal type `array of %t'\n", ty); + return array(inttype, n, 0); + } + if (isarray(ty) && ty->size == 0) + error("missing array size\n"); + if (ty->size == 0) { + if (unqual(ty) == voidtype) + error("illegal type `array of %t'\n", ty); + else if (Aflag >= 2) + warning("declaring type array of %t' is undefined\n", ty); + + } else if (n > INT_MAX/ty->size) { + error("size of `array of %t' exceeds %d bytes\n", + ty, INT_MAX); + n = 1; + } + return type(ARRAY, ty, n*ty->size, + a ? a : ty->align, NULL); +} +Type atop(Type ty) { + if (isarray(ty)) + return ptr(ty->type); + error("type error: %s\n", "array expected"); + return ptr(ty); +} +Type qual(int op, Type ty) { + if (isarray(ty)) + ty = type(ARRAY, qual(op, ty->type), ty->size, + ty->align, NULL); + else if (isfunc(ty)) + warning("qualified function type ignored\n"); + else if ((isconst(ty) && op == CONST) + || (isvolatile(ty) && op == VOLATILE)) + error("illegal type `%k %t'\n", op, ty); + else { + if (isqual(ty)) { + op += ty->op; + ty = ty->type; + } + ty = type(op, ty, ty->size, ty->align, NULL); + } + return ty; +} +Type func(Type ty, Type *proto, int style) { + if (ty && (isarray(ty) || isfunc(ty))) + error("illegal return type `%t'\n", ty); + ty = type(FUNCTION, ty, 0, 0, NULL); + ty->u.f.proto = proto; + ty->u.f.oldstyle = style; + return ty; +} +Type freturn(Type ty) { + if (isfunc(ty)) + return ty->type; + error("type error: %s\n", "function expected"); + return inttype; +} +int variadic(Type ty) { + if (isfunc(ty) && ty->u.f.proto) { + int i; + for (i = 0; ty->u.f.proto[i]; i++) + ; + return i > 1 && ty->u.f.proto[i-1] == voidtype; + } + return 0; +} +Type newstruct(int op, char *tag) { + Symbol p; + + assert(tag); + if (*tag == 0) + tag = stringd(genlabel(1)); + else + if ((p = lookup(tag, types)) != NULL && (p->scope == level + || (p->scope == PARAM && level == PARAM+1))) { + if (p->type->op == op && !p->defined) + return p->type; + error("redefinition of `%s' previously defined at %w\n", + p->name, &p->src); + } + p = install(tag, &types, level, PERM); + p->type = type(op, NULL, 0, 0, p); + if (p->scope > maxlevel) + maxlevel = p->scope; + p->src = src; + return p->type; +} +Field newfield(char *name, Type ty, Type fty) { + Field p, *q = &ty->u.sym->u.s.flist; + + if (name == NULL) + name = stringd(genlabel(1)); + for (p = *q; p; q = &p->link, p = *q) + if (p->name == name) + error("duplicate field name `%s' in `%t'\n", + name, ty); + NEW0(p, PERM); + *q = p; + p->name = name; + p->type = fty; + if (xref) { /* omit */ + if (ty->u.sym->u.s.ftab == NULL) /* omit */ + ty->u.sym->u.s.ftab = table(NULL, level); /* omit */ + install(name, &ty->u.sym->u.s.ftab, 0, PERM)->src = src;/* omit */ + } /* omit */ + return p; +} +int eqtype(Type ty1, Type ty2, int ret) { + if (ty1 == ty2) + return 1; + if (ty1->op != ty2->op) + return 0; + switch (ty1->op) { + case ENUM: case UNION: case STRUCT: + case UNSIGNED: case INT: case FLOAT: + return 0; + case POINTER: return eqtype(ty1->type, ty2->type, 1); + case VOLATILE: case CONST+VOLATILE: + case CONST: return eqtype(ty1->type, ty2->type, 1); + case ARRAY: if (eqtype(ty1->type, ty2->type, 1)) { + if (ty1->size == ty2->size) + return 1; + if (ty1->size == 0 || ty2->size == 0) + return ret; + } + return 0; + case FUNCTION: if (eqtype(ty1->type, ty2->type, 1)) { + Type *p1 = ty1->u.f.proto, *p2 = ty2->u.f.proto; + if (p1 == p2) + return 1; + if (p1 && p2) { + for ( ; *p1 && *p2; p1++, p2++) + if (eqtype(unqual(*p1), unqual(*p2), 1) == 0) + return 0; + if (*p1 == NULL && *p2 == NULL) + return 1; + } else { + if (variadic(p1 ? ty1 : ty2)) + return 0; + if (p1 == NULL) + p1 = p2; + for ( ; *p1; p1++) { + Type ty = unqual(*p1); + if (promote(ty) != (isenum(ty) ? ty->type : ty)) + return 0; + } + return 1; + } + } + return 0; + } + assert(0); return 0; +} +Type promote(Type ty) { + ty = unqual(ty); + switch (ty->op) { + case ENUM: + return inttype; + case INT: + if (ty->size < inttype->size) + return inttype; + break; + case UNSIGNED: + if (ty->size < inttype->size) + return inttype; + if (ty->size < unsignedtype->size) + return unsignedtype; + break; + case FLOAT: + if (ty->size < doubletype->size) + return doubletype; + } + return ty; +} +Type signedint(Type ty) { + if (ty->op == INT) + return ty; + assert(ty->op == UNSIGNED); +#define xx(t) if (ty->size == t->size) return t + xx(inttype); + xx(longtype); + xx(longlong); +#undef xx + assert(0); return NULL; +} +Type compose(Type ty1, Type ty2) { + if (ty1 == ty2) + return ty1; + assert(ty1->op == ty2->op); + switch (ty1->op) { + case POINTER: + return ptr(compose(ty1->type, ty2->type)); + case CONST+VOLATILE: + return qual(CONST, qual(VOLATILE, + compose(ty1->type, ty2->type))); + case CONST: case VOLATILE: + return qual(ty1->op, compose(ty1->type, ty2->type)); + case ARRAY: { Type ty = compose(ty1->type, ty2->type); + if (ty1->size && ((ty1->type->size && ty2->size == 0) || ty1->size == ty2->size)) + return array(ty, ty1->size/ty1->type->size, ty1->align); + if (ty2->size && ty2->type->size && ty1->size == 0) + return array(ty, ty2->size/ty2->type->size, ty2->align); + return array(ty, 0, 0); } + case FUNCTION: { Type *p1 = ty1->u.f.proto, *p2 = ty2->u.f.proto; + Type ty = compose(ty1->type, ty2->type); + List tlist = NULL; + if (p1 == NULL && p2 == NULL) + return func(ty, NULL, 1); + if (p1 && p2 == NULL) + return func(ty, p1, ty1->u.f.oldstyle); + if (p2 && p1 == NULL) + return func(ty, p2, ty2->u.f.oldstyle); + for ( ; *p1 && *p2; p1++, p2++) { + Type ty = compose(unqual(*p1), unqual(*p2)); + if (isconst(*p1) || isconst(*p2)) + ty = qual(CONST, ty); + if (isvolatile(*p1) || isvolatile(*p2)) + ty = qual(VOLATILE, ty); + tlist = append(ty, tlist); + } + assert(*p1 == NULL && *p2 == NULL); + return func(ty, ltov(&tlist, PERM), 0); } + } + assert(0); return NULL; +} +int ttob(Type ty) { + switch (ty->op) { + case CONST: case VOLATILE: case CONST+VOLATILE: + return ttob(ty->type); + case VOID: case INT: case UNSIGNED: case FLOAT: + return ty->op + sizeop(ty->size); + case POINTER: + return POINTER + sizeop(voidptype->size); + case FUNCTION: + return POINTER + sizeop(funcptype->size); + case ARRAY: case STRUCT: case UNION: + return STRUCT; + case ENUM: + return INT + sizeop(inttype->size); + } + assert(0); return INT; +} +Type btot(int op, int size) { +#define xx(ty) if (size == (ty)->size) return ty; + switch (optype(op)) { + case F: + xx(floattype); + xx(doubletype); + xx(longdouble); + assert(0); return 0; + case I: + if (chartype->op == INT) + xx(chartype); + xx(signedchar); + xx(shorttype); + xx(inttype); + xx(longtype); + xx(longlong); + assert(0); return 0; + case U: + if (chartype->op == UNSIGNED) + xx(chartype); + xx(unsignedchar); + xx(unsignedshort); + xx(unsignedtype); + xx(unsignedlong); + xx(unsignedlonglong); + assert(0); return 0; + case P: + xx(voidptype); + xx(funcptype); + assert(0); return 0; + } +#undef xx + assert(0); return 0; +} +int hasproto(Type ty) { + if (ty == 0) + return 1; + switch (ty->op) { + case CONST: case VOLATILE: case CONST+VOLATILE: case POINTER: + case ARRAY: + return hasproto(ty->type); + case FUNCTION: + return hasproto(ty->type) && ty->u.f.proto; + case STRUCT: case UNION: + case VOID: case FLOAT: case ENUM: case INT: case UNSIGNED: + return 1; + } + assert(0); return 0; +} +/* fieldlist - construct a flat list of fields in type ty */ +Field fieldlist(Type ty) { + return ty->u.sym->u.s.flist; +} + +/* fieldref - find field name of type ty, return entry */ +Field fieldref(const char *name, Type ty) { + Field p = isfield(name, unqual(ty)->u.sym->u.s.flist); + + if (p && xref) { + Symbol q; + assert(unqual(ty)->u.sym->u.s.ftab); + q = lookup(name, unqual(ty)->u.sym->u.s.ftab); + assert(q); + use(q, src); + } + return p; +} + +/* ftype - return a function type for rty function (ty,...)' */ +Type ftype(Type rty, Type ty) { + List list = append(ty, NULL); + + list = append(voidtype, list); + return func(rty, ltov(&list, PERM), 0); +} + +/* isfield - if name is a field in flist, return pointer to the field structure */ +static Field isfield(const char *name, Field flist) { + for ( ; flist; flist = flist->link) + if (flist->name == name) + break; + return flist; +} + +/* outtype - output type ty */ +void outtype(Type ty, FILE *f) { + switch (ty->op) { + case CONST+VOLATILE: case CONST: case VOLATILE: + fprint(f, "%k %t", ty->op, ty->type); + break; + case STRUCT: case UNION: case ENUM: + assert(ty->u.sym); + if (ty->size == 0) + fprint(f, "incomplete "); + assert(ty->u.sym->name); + if (*ty->u.sym->name >= '1' && *ty->u.sym->name <= '9') { + Symbol p = findtype(ty); + if (p == 0) + fprint(f, "%k defined at %w", ty->op, &ty->u.sym->src); + else + fprint(f, p->name); + } else { + fprint(f, "%k %s", ty->op, ty->u.sym->name); + if (ty->size == 0) + fprint(f, " defined at %w", &ty->u.sym->src); + } + break; + case VOID: case FLOAT: case INT: case UNSIGNED: + fprint(f, ty->u.sym->name); + break; + case POINTER: + fprint(f, "pointer to %t", ty->type); + break; + case FUNCTION: + fprint(f, "%t function", ty->type); + if (ty->u.f.proto && ty->u.f.proto[0]) { + int i; + fprint(f, "(%t", ty->u.f.proto[0]); + for (i = 1; ty->u.f.proto[i]; i++) + if (ty->u.f.proto[i] == voidtype) + fprint(f, ",..."); + else + fprint(f, ",%t", ty->u.f.proto[i]); + fprint(f, ")"); + } else if (ty->u.f.proto && ty->u.f.proto[0] == 0) + fprint(f, "(void)"); + + break; + case ARRAY: + if (ty->size > 0 && ty->type && ty->type->size > 0) { + fprint(f, "array %d", ty->size/ty->type->size); + while (ty->type && isarray(ty->type) && ty->type->type->size > 0) { + ty = ty->type; + fprint(f, ",%d", ty->size/ty->type->size); + } + } else + fprint(f, "incomplete array"); + if (ty->type) + fprint(f, " of %t", ty->type); + break; + default: assert(0); + } +} + +/* printdecl - output a C declaration for symbol p of type ty */ +void printdecl(Symbol p, Type ty) { + switch (p->sclass) { + case AUTO: + fprint(stderr, "%s;\n", typestring(ty, p->name)); + break; + case STATIC: case EXTERN: + fprint(stderr, "%k %s;\n", p->sclass, typestring(ty, p->name)); + break; + case TYPEDEF: case ENUM: + break; + default: assert(0); + } +} + +/* printproto - output a prototype declaration for function p */ +void printproto(Symbol p, Symbol callee[]) { + if (p->type->u.f.proto) + printdecl(p, p->type); + else { + int i; + List list = 0; + if (callee[0] == 0) + list = append(voidtype, list); + else + for (i = 0; callee[i]; i++) + list = append(callee[i]->type, list); + printdecl(p, func(freturn(p->type), ltov(&list, PERM), 0)); + } +} + +/* prtype - print details of type ty on f with given indent */ +static void prtype(Type ty, FILE *f, int indent, unsigned mark) { + switch (ty->op) { + default: + fprint(f, "(%d %d %d [%p])", ty->op, ty->size, ty->align, ty->u.sym); + break; + case FLOAT: case INT: case UNSIGNED: case VOID: + fprint(f, "(%k %d %d [\"%s\"])", ty->op, ty->size, ty->align, ty->u.sym->name); + break; + case CONST+VOLATILE: case CONST: case VOLATILE: case POINTER: case ARRAY: + fprint(f, "(%k %d %d ", ty->op, ty->size, ty->align); + prtype(ty->type, f, indent+1, mark); + fprint(f, ")"); + break; + case STRUCT: case UNION: + fprint(f, "(%k %d %d [\"%s\"]", ty->op, ty->size, ty->align, ty->u.sym->name); + if (ty->x.marked != mark) { + Field p; + ty->x.marked = mark; + for (p = ty->u.sym->u.s.flist; p; p = p->link) { + fprint(f, "\n%I", indent+1); + prtype(p->type, f, indent+1, mark); + fprint(f, " %s@%d", p->name, p->offset); + if (p->lsb) + fprint(f, ":%d..%d", + fieldsize(p) + fieldright(p), fieldright(p)); + } + fprint(f, "\n%I", indent); + } + fprint(f, ")"); + break; + case ENUM: + fprint(f, "(%k %d %d [\"%s\"]", ty->op, ty->size, ty->align, ty->u.sym->name); + if (ty->x.marked != mark) { + int i; + Symbol *p = ty->u.sym->u.idlist; + ty->x.marked = mark; + for (i = 0; p[i] != NULL; i++) + fprint(f, "%I%s=%d\n", indent+1, p[i]->name, p[i]->u.value); + } + fprint(f, ")"); + break; + case FUNCTION: + fprint(f, "(%k %d %d ", ty->op, ty->size, ty->align); + prtype(ty->type, f, indent+1, mark); + if (ty->u.f.proto) { + int i; + fprint(f, "\n%I{", indent+1); + for (i = 0; ty->u.f.proto[i]; i++) { + if (i > 0) + fprint(f, "%I", indent+2); + prtype(ty->u.f.proto[i], f, indent+2, mark); + fprint(f, "\n"); + } + fprint(f, "%I}", indent+1); + } + fprint(f, ")"); + break; + } +} + +/* printtype - print details of type ty on fd */ +void printtype(Type ty, int fd) { + static unsigned mark; + prtype(ty, fd == 1 ? stdout : stderr, 0, ++mark); + fprint(fd == 1 ? stdout : stderr, "\n"); +} + +/* typestring - return ty as C declaration for str, which may be "" */ +char *typestring(Type ty, char *str) { + for ( ; ty; ty = ty->type) { + Symbol p; + switch (ty->op) { + case CONST+VOLATILE: case CONST: case VOLATILE: + if (isptr(ty->type)) + str = stringf("%k %s", ty->op, str); + else + return stringf("%k %s", ty->op, typestring(ty->type, str)); + break; + case STRUCT: case UNION: case ENUM: + assert(ty->u.sym); + if ((p = findtype(ty)) != NULL) + return *str ? stringf("%s %s", p->name, str) : p->name; + if (*ty->u.sym->name >= '1' && *ty->u.sym->name <= '9') + warning("unnamed %k in prototype\n", ty->op); + if (*str) + return stringf("%k %s %s", ty->op, ty->u.sym->name, str); + else + return stringf("%k %s", ty->op, ty->u.sym->name); + case VOID: case FLOAT: case INT: case UNSIGNED: + return *str ? stringf("%s %s", ty->u.sym->name, str) : ty->u.sym->name; + case POINTER: + if (!ischar(ty->type) && (p = findtype(ty)) != NULL) + return *str ? stringf("%s %s", p->name, str) : p->name; + str = stringf(isarray(ty->type) || isfunc(ty->type) ? "(*%s)" : "*%s", str); + break; + case FUNCTION: + if ((p = findtype(ty)) != NULL) + return *str ? stringf("%s %s", p->name, str) : p->name; + if (ty->u.f.proto == 0) + str = stringf("%s()", str); + else if (ty->u.f.proto[0]) { + int i; + str = stringf("%s(%s", str, typestring(ty->u.f.proto[0], "")); + for (i = 1; ty->u.f.proto[i]; i++) + if (ty->u.f.proto[i] == voidtype) + str = stringf("%s, ...", str); + else + str = stringf("%s, %s", str, typestring(ty->u.f.proto[i], "")); + str = stringf("%s)", str); + } else + str = stringf("%s(void)", str); + break; + case ARRAY: + if ((p = findtype(ty)) != NULL) + return *str ? stringf("%s %s", p->name, str) : p->name; + if (ty->type && ty->type->size > 0) + str = stringf("%s[%d]", str, ty->size/ty->type->size); + else + str = stringf("%s[]", str); + break; + default: assert(0); + } + } + assert(0); return 0; +} + |