diff --git a/src/SDCC.lex b/src/SDCC.lex index 327a27239..0ac50a32b 100644 --- a/src/SDCC.lex +++ b/src/SDCC.lex @@ -27,9 +27,24 @@ D [0-9] L [a-zA-Z_$] H [a-fA-F0-9] E [Ee][+-]?{D}+ +BE [Pp][+-]?{D}+ FS (f|F|l|L) IS (u|U|l|L)* +CP (L|u|U|u8) HASH (#|%:) +UCN \\u{H}{4}|\\U{H}{8} + +UTF8PART1 \xc2[\xa8\xaa\xad\xaf\xb2-\xb5\xb7-\xba\xbc-\xbe]|\xc3[\x80-\x96\x98-\xb6\xb8-\xbf]|[\xc4-\xcb\xce-\xdf][\x80-\xbf]|\xcd[\xb0-\xbf] +UTF8PART2 \xe0[\xa0-\xbf][\x80-\xbf]|\xe1([\x80-\x99\x9b-\x9f\xa1-\xb6\xb8-\xbf][\x80-\xbf]|\x9a[\x81-\xbf]|\xa0[\x80-\x8d\x8f-\xbf]) +UTF8PART3 \xe2(\x80[\x8b-\x8d\xaa-\xae\xbf]|\x81[\x80\x94\xa0-\xbf]|\x82[\x80-\xbf]|[\x83\x86][\x80-\x8f]|[\x84-\x85\x92-\x93\xb0-\xb7\xba-\xbf][\x80-\xbf]|\x91[\xa0-\xbf]|\x9d[\xb6-\xbf]|\x9e[\x80-\x93]) +UTF8PART4 \xe3(\x80[\x84-\x87\xa1-\xaf\xb1-\xbf]|[\x81-\xbf][\x80-\xbf])|[\xe4-\xec][\x80-\xbf][\x80-\xbf]|\xed[\x80-\x9f][\x80-\xbf] +UTF8PART5 \xef([\xa4-\xb3\xb5-\xb6\xba-\xbe][\x80-\xbf]|\xb4[\x80-\xbd]|\xb7[\x80-\x8f]|[\xb7-\xb8][\xb0-\xbf]|\xb8[\x80-\x9f]|\xb9[\x80-\x84\x87-\xbf]|\xbf[\x80-\xbd]) +UTF8PART6 \xf0([\x90-\x9e\xa0-\xae\xb0-\xbe][\x80-\xbf][\x80-\xbf]|[\x9f\xaf\xbf]([\x80-\xbe][\x80-\xbf]|\xbf[\x80-\xbd])) +UTF8PART7 [\xf1-\xf2]([\x80-\x8e\x90-\x9e\xa0-\xae\xb0-\xbe][\x80-\xbf][\x80-\xbf]|[\x8f\x9f\xaf\xbf]([\x80-\xbe][\x80-\xbf]|\xbf[\x80-\xbd])) +UTF8PART8 \xf3([\x80-\x8e\x90-\x9e\xa0-\xae][\x80-\xbf][\x80-\xbf]|[\x8f\x9f\xaf]([\x80-\xbe][\x80-\xbf]|\xbf[\x80-\xbd])) + +UTF8IDF1ST {UTF8PART1}|{UTF8PART2}|{UTF8PART3}|{UTF8PART4}|{UTF8PART5}|{UTF8PART6}|{UTF8PART7}|{UTF8PART8} +UTF8IDF {UTF8IDF1ST}|\xcc[\x80-\xbf]|\xcd[\x80-\xaf]|\xe2\x83[\x90-\xbf]|\xef\xb8[\xa0-\xaf]|\xe1\xb7[\x80-\xbf] %{ #include @@ -38,6 +53,15 @@ HASH (#|%:) #include "common.h" #include "newalloc.h" #include "dbuf_string.h" +/* Some systems, noteably Mac OS, do not have uchar.h. */ +/* If it is missing, use our own type definitions. */ +#ifdef HAVE_UCHAR_H +#include +#else +#include +#define char16_t uint_least16_t +#define char32_t uint_least32_t +#endif /* Needed by flex 2.5.4 on NetBSD 5.0.1 sparc64 */ #ifdef HAVE_UNISTD_H #include @@ -47,10 +71,12 @@ HASH (#|%:) # include #endif -#define TKEYWORD(token) return (isTargetKeyword(yytext) ? token :\ +#define TKEYWORD(token) return (isTargetKeyword(yytext) ? (token) :\ check_type()) -#define TKEYWORD99(token) return (options.std_c99 ? token : check_type()) +#define TKEYWORD99(token) return (options.std_c99 ? (token) : check_type()) + +#define TKEYWORD2X(token) return (options.std_c2x ? (token) : check_type()) int column = 0; /* current column */ @@ -63,12 +89,11 @@ static struct dbuf_s asmbuff; /* reusable _asm buffer */ /* forward declarations */ int yyerror (char *s); -static const char *stringLiteral (void); +static const char *stringLiteral (char); static void count (void); static void count_char (int); static int process_pragma (const char *); static int check_type (void); -static int isTargetKeyword (const char *s); static void checkCurrFile (const char *s); %} @@ -100,6 +125,7 @@ static void checkCurrFile (const char *s); "__at" { count (); TKEYWORD (AT); } "auto" { count (); return AUTO; } "__bit" { count (); TKEYWORD (BIT); } +"bool" { count (); TKEYWORD2X (SD_BOOL); } "_Bool" { count (); TKEYWORD99 (SD_BOOL); } "break" { count (); return BREAK; } "case" { count (); return CASE; } @@ -111,7 +137,7 @@ static void checkCurrFile (const char *s); "__data" { count (); TKEYWORD (DATA); } "default" { count (); return DEFAULT; } "do" { count (); return DO; } -"double" { count (); return SD_FLOAT; } /* NOTE: Doubles are unsupported (just floats) */ +"double" { count (); werror (W_DOUBLE_UNSUPPORTED); return SD_FLOAT; } "else" { count (); return ELSE; } "enum" { count (); return ENUM; } "extern" { count (); return EXTERN; } @@ -128,10 +154,10 @@ static void checkCurrFile (const char *s); "__interrupt" { count (); TKEYWORD (INTERRUPT); } "__nonbanked" { count (); TKEYWORD (NONBANKED); } "__banked" { count (); TKEYWORD (BANKED); } +"__trap" { count (); TKEYWORD (TRAP); } "long" { count (); return SD_LONG; } "__near" { count (); TKEYWORD (DATA); } "__pdata" { count (); TKEYWORD (PDATA); } -"__reentrant" { count (); TKEYWORD (REENTRANT); } "__shadowregs" { count (); TKEYWORD (SHADOWREGS); } "__wparam" { count (); TKEYWORD (SD_WPARAM); } "register" { count (); return REGISTER; } @@ -143,12 +169,14 @@ static void checkCurrFile (const char *s); "short" { count (); return SD_SHORT; } "signed" { count (); return SIGNED; } "sizeof" { count (); return SIZEOF; } +"alignof" { count (); TKEYWORD2X (ALIGNOF); } "_Alignof" { count (); return ALIGNOF; } "__builtin_offsetof" { count (); return OFFSETOF; } "__sram" { count (); TKEYWORD (XDATA); } "static" { count (); return STATIC; } "struct" { count (); return STRUCT; } "switch" { count (); return SWITCH; } +"_Thread_local" { count (); return THREAD_LOCAL; } "typedef" { count (); return TYPEDEF; } "union" { count (); return UNION; } "unsigned" { count (); return UNSIGNED; } @@ -165,35 +193,59 @@ static void checkCurrFile (const char *s); "inline" { count (); TKEYWORD99 (INLINE); } "_Noreturn" { count (); return NORETURN;} "restrict" { count (); TKEYWORD99 (RESTRICT); } -"__smallc" { count (); return SMALLC; } +"__smallc" { count (); TKEYWORD (SMALLC); } +"__preserves_regs" { count (); return PRESERVES_REGS; } +"__z88dk_fastcall" { count (); TKEYWORD (Z88DK_FASTCALL); } +"__z88dk_callee" { count (); TKEYWORD (Z88DK_CALLEE); } +"__z88dk_shortcall" { count (); return Z88DK_SHORTCALL; } +"__z88dk_params_offset" { count (); return Z88DK_PARAMS_OFFSET; } "__addressmod" { count (); return ADDRESSMOD; } +"static_assert" { count (); TKEYWORD2X (STATIC_ASSERT); } "_Static_assert" { count (); return STATIC_ASSERT; } +"alignas" { count (); TKEYWORD2X (ALIGNAS); } "_Alignas" { count (); return ALIGNAS; } -{L}({L}|{D})* { +"_Generic" { count (); return GENERIC; } +({L}|{UCN}|{UTF8IDF1ST})({L}|{D}|{UCN}|{UTF8IDF})* { if (!options.dollars_in_ident && strchr (yytext, '$')) { yyerror ("stray '$' in program"); } + if (!options.std_c95) + { + bool ucn_check = strchr (yytext, '\\'); + for (char *ptr = yytext; *ptr && !ucn_check; ptr++) + { + if ((unsigned char) *ptr >= 0x80) + ucn_check = true; + } + if (ucn_check) + werror (W_UNIVERSAL_C95); + } count (); return check_type(); } 0[bB]{B}+{IS}? { - if (!options.std_sdcc) - { - yyerror ("binary (0b) constants are not allowed in ISO C"); - } + if (!options.std_sdcc && !options.std_c2x) + werror (W_BINARY_INTEGER_CONSTANT_C23); count (); - yylval.val = constVal (yytext); + yylval.val = constIntVal (yytext); return CONSTANT; } -0[xX]{H}+{IS}? { count (); yylval.val = constVal (yytext); return CONSTANT; } -0[0-7]*{IS}? { count (); yylval.val = constVal (yytext); return CONSTANT; } -[1-9]{D}*{IS}? { count (); yylval.val = constVal (yytext); return CONSTANT; } -'(\\.|[^\\'])+' { count (); yylval.val = charVal (yytext); return CONSTANT; /* ' make syntax highliter happy */ } -{D}+{E}{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } -{D}*"."{D}+({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } -{D}+"."{D}*({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } -\" { count (); yylval.yystr = stringLiteral (); return STRING_LITERAL; } +0[xX]{H}+{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +0[0-7]*{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +[1-9]{D}*{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +{CP}?'(\\.|[^\\'])+' { count (); yylval.val = charVal (yytext); return CONSTANT; /* ' make syntax highlighter happy */ } +{D}+{E}{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +{D}*"."{D}+({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +{D}+"."{D}*({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}+{BE}{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}*"."{H}+({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}+"."{H}*({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +\" { count (); yylval.yystr = stringLiteral (0); return STRING_LITERAL; } +"L\"" { count (); if (!options.std_c95) werror(E_WCHAR_STRING_C95); yylval.yystr = stringLiteral ('L'); return STRING_LITERAL; } +"u8\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral (0); return STRING_LITERAL; } +"u\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('u'); return STRING_LITERAL; } +"U\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('U'); return STRING_LITERAL; } ">>=" { count (); yylval.yyint = RIGHT_ASSIGN; return RIGHT_ASSIGN; } "<<=" { count (); yylval.yyint = LEFT_ASSIGN; return LEFT_ASSIGN; } "+=" { count (); yylval.yyint = ADD_ASSIGN; return ADD_ASSIGN; } @@ -239,6 +291,7 @@ static void checkCurrFile (const char *s); "^" { count (); return '^'; } "|" { count (); return '|'; } "?" { count (); return '?'; } +"::" { count (); return ATTRIBCOLON; } ^{HASH}pragma.* { count (); process_pragma (yytext); } ^{HASH}.* { count (); checkCurrFile (yytext); } @@ -358,19 +411,126 @@ count (void) count_char(*p); } +static bool +is_UCN_valid_in_idf (char32_t c, bool is_first) +{ + bool result = false; + + // D.1 Ranges of characters allowed + if ((c == 0x00A8) || (c == 0x00AA) || (c == 0x00AD) || (c == 0x00AF) + || (c >= 0x00B2 && c <= 0x00B5) || (c >= 0x00B7 && c <= 0x00BA) + || (c >= 0x00BC && c <= 0x00BE) || (c >= 0x00C0 && c <= 0x00D6) + || (c >= 0x00D8 && c <= 0x00F6) || (c >= 0x00F8 && c <= 0x00FF) + || (c >= 0x0100 && c <= 0x167F) || (c >= 0x1681 && c <= 0x180D) + || (c >= 0x180F && c <= 0x1FFF) || (c >= 0x200B && c <= 0x200D) + || (c >= 0x202A && c <= 0x202E) || (c >= 0x203F && c <= 0x2040) + || (c == 0x2054) || (c >= 0x2060 && c <= 0x206F) + || (c >= 0x2070 && c <= 0x218F) || (c >= 0x2460 && c <= 0x24FF) + || (c >= 0x2776 && c <= 0x2793) || (c >= 0x2C00 && c <= 0x2DFF) + || (c >= 0x2E80 && c <= 0x2FFF) || (c >= 0x3004 && c <= 0x3007) + || (c >= 0x3021 && c <= 0x302F) || (c >= 0x3031 && c <= 0x303F) + || (c >= 0x3040 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFD3D) + || (c >= 0xFD40 && c <= 0xFDCF) || (c >= 0xFDF0 && c <= 0xFE44) + || (c >= 0xFE47 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x1FFFD) + || (c >= 0x20000 && c <= 0x2FFFD) || (c >= 0x30000 && c <= 0x3FFFD) + || (c >= 0x40000 && c <= 0x4FFFD) || (c >= 0x50000 && c <= 0x5FFFD) + || (c >= 0x60000 && c <= 0x6FFFD) || (c >= 0x70000 && c <= 0x7FFFD) + || (c >= 0x80000 && c <= 0x8FFFD) || (c >= 0x90000 && c <= 0x9FFFD) + || (c >= 0xA0000 && c <= 0xAFFFD) || (c >= 0xB0000 && c <= 0xBFFFD) + || (c >= 0xC0000 && c <= 0xCFFFD) || (c >= 0xD0000 && c <= 0xDFFFD) + || (c >= 0xE0000 && c <= 0xEFFFD)) + { + result = true; + // D.2 Ranges of characters disallowed initially + if (is_first && ((c >= 0x0300 && c <= 0x036F) || (c >= 0x1DC0 && c <= 0x1DFF) + || (c >= 0x20D0 && c <= 0x20FF) || (c >= 0xFE20 && c <= 0xFE2F))) + { + result = false; + } + } + + return result; +} + +static void +decode_UCNs_to_utf8 (char *dest, const char *src, size_t n) +{ + bool is_first = true; + const char *s = src; + size_t chars_left = n - 1; + + while (*src) + { + if (*src == '\\') + { + ++src; + char32_t c = 0; + if (*src == 'u') + { + c = universalEscape(&src, 4); + } + else // U - the lexer only accepts \u and \U escapes in identifiers + { + c = universalEscape(&src, 8); + } + if (!is_UCN_valid_in_idf(c, is_first)) + { + werror(E_INVALID_UNIVERSAL, s); + } + + if (c >= 0x10000) + { + if (chars_left < 4) + break; + *(dest++) = 0xf0 | (c >> 18); + *(dest++) = 0x80 | ((c >> 12) & 0x3f); + *(dest++) = 0x80 | ((c >> 6) & 0x3f); + *(dest++) = 0x80 | (c & 0x3f); + chars_left -= 4; + } + else if (c >= 0x800) + { + if (chars_left < 3) + break; + *(dest++) = 0xe0 | (c >> 12); + *(dest++) = 0x80 | ((c >> 6) & 0x3f); + *(dest++) = 0x80 | (c & 0x3f); + chars_left -= 3; + } + else // ASCII characters already eliminated by validity check => no further check here + { + if (chars_left < 2) + break; + *(dest++) = 0xc0 | (c >> 6); + *(dest++) = 0x80 | (c & 0x3f); + chars_left -= 2; + } + } + else + { + if (chars_left < 1) + break; + *(dest++) = *(src++); + chars_left--; + } + is_first = false; + } + *dest = '\0'; +} + static int check_type (void) { - symbol *sym = findSym(SymbolTab, NULL, yytext); + decode_UCNs_to_utf8(yylval.yychar, yytext, SDCC_NAME_MAX); - strncpyz(yylval.yychar, yytext, SDCC_NAME_MAX); + symbol *sym = findSym(SymbolTab, NULL, yylval.yychar); /* check if it is in the table as a typedef */ if (!ignoreTypedefType && sym && IS_SPEC (sym->etype) - && SPEC_TYPEDEF (sym->etype) && findSym(TypedefTab, NULL, yytext)) + && SPEC_TYPEDEF (sym->etype) && findSym(TypedefTab, NULL, yylval.yychar)) return (TYPE_NAME); /* check if it is a named address space */ - else if (findSym (AddrspaceTab, NULL, yytext)) + else if (findSym (AddrspaceTab, NULL, yylval.yychar)) return (ADDRSPACE_NAME); else return(IDENTIFIER); @@ -382,7 +542,7 @@ check_type (void) */ static const char * -stringLiteral (void) +stringLiteral (char enc) { #define STR_BUF_CHUNCK_LEN 1024 int ch; @@ -393,7 +553,19 @@ stringLiteral (void) else dbuf_set_length(&dbuf, 0); - dbuf_append_char(&dbuf, '"'); + switch (enc) + { + case 'u': // UTF-16 + dbuf_append_str(&dbuf, "u\""); + break; + case 'L': + case 'U': // UTF-32 + enc = 'U'; + dbuf_append_str(&dbuf, "U\""); + break; + default: // UTF-8 or whatever else the source character set is encoded in + dbuf_append_char(&dbuf, '"'); + } /* put into the buffer till we hit the first \" */ @@ -503,6 +675,51 @@ stringLiteral (void) if (ch == EOF) goto out; + if (ch == 'u' || ch == 'U' || ch == 'L') /* Could be an utf-16 or utf-32 wide string literal prefix */ + { + int ch2; + + if (!(options.std_c11 || options.std_c95 && ch == 'L')) + { + werror (ch == 'L' ? E_WCHAR_STRING_C95 : E_WCHAR_STRING_C11); + unput(ch); + goto out; + } + + ch2 = input(); + if (ch2 != '"') + unput (ch2); + else /* It is an utf-16 or utf-32 wide string literal prefix */ + { + if (!enc) + { + dbuf_prepend_char(&dbuf, ch == 'L' ? 'U' : ch); + enc = ch; + } + count_char(ch); + count_char(ch2); + break; + } + } + + if (ch == 'u') /* Could be an utf-8 wide string literal prefix */ + { + ch = input(); + if (ch != '8') + { + unput(ch); + unput('u'); + goto out; + } + ch = input(); + if (ch != '"') + { + unput(ch); + unput('8'); + unput('u'); + goto out; + } + } if (ch != '"') { unput(ch); @@ -528,7 +745,6 @@ enum { P_NOINDUCTION, P_NOINVARIANT, P_STACKAUTO, - P_NOJTBOUND, P_OVERLAY_, /* I had a strange conflict with P_OVERLAY while */ /* cross-compiling for MINGW32 with gcc 3.2 */ P_NOOVERLAY, @@ -545,6 +761,7 @@ enum { P_STD_C89, P_STD_C99, P_STD_C11, + P_STD_C2X, P_STD_SDCC89, P_STD_SDCC99, P_CODESEG, @@ -729,17 +946,6 @@ doPragma (int id, const char *name, const char *cp) options.stackAuto = 1; break; - case P_NOJTBOUND: - cp = get_pragma_token(cp, &token); - if (TOKEN_EOL != token.type) - { - err = 1; - break; - } - - optimize.noJTabBoundary = 1; - break; - case P_NOGCSE: cp = get_pragma_token(cp, &token); if (TOKEN_EOL != token.type) @@ -890,6 +1096,7 @@ doPragma (int id, const char *name, const char *cp) options.std_c99 = 0; options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 0; break; @@ -902,6 +1109,8 @@ doPragma (int id, const char *name, const char *cp) } options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 0; break; @@ -915,6 +1124,21 @@ doPragma (int id, const char *name, const char *cp) options.std_c99 = 1; options.std_c11 = 1; + options.std_c2x = 0; + options.std_sdcc = 0; + break; + + case P_STD_C2X: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 1; options.std_sdcc = 0; break; @@ -928,6 +1152,7 @@ doPragma (int id, const char *name, const char *cp) options.std_c99 = 0; options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 1; break; @@ -941,6 +1166,7 @@ doPragma (int id, const char *name, const char *cp) options.std_c99 = 1; options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 1; break; @@ -996,7 +1222,6 @@ static struct pragma_s pragma_tbl[] = { { "noinvariant", P_NOINVARIANT, 0, doPragma }, { "noloopreverse", P_LOOPREV, 0, doPragma }, { "stackauto", P_STACKAUTO, 0, doPragma }, - { "nojtbound", P_NOJTBOUND, 0, doPragma }, { "nogcse", P_NOGCSE, 0, doPragma }, { "overlay", P_OVERLAY_, 0, doPragma }, { "nooverlay", P_NOOVERLAY, 0, doPragma }, @@ -1011,6 +1236,7 @@ static struct pragma_s pragma_tbl[] = { { "std_c89", P_STD_C89, 0, doPragma }, { "std_c99", P_STD_C99, 0, doPragma }, { "std_c11", P_STD_C11, 0, doPragma }, + { "std_c2x", P_STD_C2X, 0, doPragma }, { "std_sdcc89", P_STD_SDCC89, 0, doPragma }, { "std_sdcc99", P_STD_SDCC99, 0, doPragma }, { "codeseg", P_CODESEG, 0, doPragma }, @@ -1086,41 +1312,6 @@ process_pragma (const char *s) } } -/* will return 1 if the string is a part - of a target specific keyword */ -static int -isTargetKeyword (const char *s) -{ - int i; - - if (port->keywords == NULL) - return 0; - - if (s[0] == '_' && s[1] == '_') - { - /* Keywords in the port's array have either 0 or 1 underscore, */ - /* so skip over the appropriate number of chars when comparing */ - for (i = 0 ; port->keywords[i] ; i++ ) - { - if (port->keywords[i][0] == '_' && - strcmp(port->keywords[i],s+1) == 0) - return 1; - else if (strcmp(port->keywords[i],s+2) == 0) - return 1; - } - } - else - { - for (i = 0 ; port->keywords[i] ; i++ ) - { - if (strcmp(port->keywords[i],s) == 0) - return 1; - } - } - - return 0; -} - int yywrap (void) { diff --git a/src/SDCC.y b/src/SDCC.y index 3bb90c54a..a71ae1ed7 100644 --- a/src/SDCC.y +++ b/src/SDCC.y @@ -40,19 +40,20 @@ extern int yyerror (char *); extern FILE *yyin; -int NestLevel = 0; /* current NestLevel */ +long NestLevel = 0; /* current NestLevel */ int stackPtr = 1; /* stack pointer */ int xstackPtr = 0; /* xstack pointer */ -int reentrant = 0; int blockNo = 0; /* sequential block number */ int currBlockno=0; -int inCritical= 0; +int inCriticalFunction = 0; +int inCriticalBlock = 0; int seqPointNo= 1; /* sequence point number */ int ignoreTypedefType=0; extern int yylex(); int yyparse(void); extern int noLineno; char lbuff[1024]; /* local buffer */ +char function_name[256] = {0}; /* break & continue stacks */ STACK_DCL(continueStack ,symbol *,MAX_NEST_LEVEL) @@ -67,7 +68,7 @@ bool uselessDecl = TRUE; #define YYDEBUG 1 %} -%expect 6 +%expect 11 %union { symbol *sym; /* symbol table pointer */ @@ -87,37 +88,41 @@ bool uselessDecl = TRUE; %token SIZEOF ALIGNOF TYPEOF OFFSETOF %token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP %token AND_OP OR_OP +%token ATTRIBCOLON %token MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN %token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN %token XOR_ASSIGN OR_ASSIGN -%token TYPEDEF EXTERN STATIC AUTO REGISTER CODE EEPROM INTERRUPT SFR SFR16 SFR32 ADDRESSMOD STATIC_ASSERT +%token TYPEDEF EXTERN STATIC THREAD_LOCAL AUTO REGISTER CODE EEPROM INTERRUPT SFR SFR16 SFR32 ADDRESSMOD STATIC_ASSERT %token AT SBIT REENTRANT USING XDATA DATA IDATA PDATA VAR_ARGS CRITICAL %token NONBANKED BANKED SHADOWREGS SD_WPARAM %token SD_BOOL SD_CHAR SD_SHORT SD_INT SD_LONG SIGNED UNSIGNED SD_FLOAT DOUBLE FIXED16X16 SD_CONST VOLATILE SD_VOID BIT %token STRUCT UNION ENUM RANGE SD_FAR %token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN -%token NAKED JAVANATIVE OVERLAY +%token NAKED JAVANATIVE OVERLAY TRAP %token STRING_LITERAL INLINEASM -%token IFX ADDRESS_OF GET_VALUE_AT_ADDRESS SPIL UNSPIL GETHBIT GETABIT GETBYTE GETWORD +%token IFX ADDRESS_OF GET_VALUE_AT_ADDRESS SET_VALUE_AT_ADDRESS SPIL UNSPIL GETHBIT GETABIT GETBYTE GETWORD %token BITWISEAND UNARYMINUS IPUSH IPOP PCALL ENDFUNCTION JUMPTABLE %token RRC RLC %token CAST CALL PARAM NULLOP BLOCK LABEL RECEIVE SEND ARRAYINIT -%token DUMMY_READ_VOLATILE ENDCRITICAL SWAP INLINE NORETURN RESTRICT SMALLC ALIGNAS +%token DUMMY_READ_VOLATILE ENDCRITICAL SWAP INLINE NORETURN RESTRICT SMALLC PRESERVES_REGS Z88DK_FASTCALL Z88DK_CALLEE ALIGNAS Z88DK_SHORTCALL Z88DK_PARAMS_OFFSET +%token GENERIC GENERIC_ASSOC_LIST GENERIC_ASSOCIATION %token ASM %type Interrupt_storage -%type identifier declarator declarator2 declarator3 enumerator_list enumerator -%type struct_declarator function_declarator function_declarator2 -%type struct_declarator_list struct_declaration struct_declaration_list +%type identifier declarator declarator2 direct_declarator array_declarator enumerator_list enumerator +%type member_declarator function_declarator +%type member_declarator_list member_declaration member_declaration_list %type declaration init_declarator_list init_declarator %type declaration_list identifier_list +%type declaration_after_statement %type declarator2_function_attributes while do for critical %type addressmod -%type pointer type_specifier_list type_specifier_list_ type_specifier type_name +%type pointer specifier_qualifier_list type_specifier_list_ type_specifier_qualifier type_specifier type_qualifier_list type_qualifier type_name %type storage_class_specifier struct_or_union_specifier function_specifier alignment_specifier %type declaration_specifiers declaration_specifiers_ sfr_reg_bit sfr_attributes %type function_attribute function_attributes enum_specifier -%type abstract_declarator abstract_declarator2 unqualified_pointer +%type abstract_declarator direct_abstract_declarator direct_abstract_declarator_opt array_abstract_declarator function_abstract_declarator +%type unqualified_pointer %type parameter_type_list parameter_list parameter_declaration opt_assign_expr %type stag opt_stag %type primary_expr @@ -130,6 +135,8 @@ bool uselessDecl = TRUE; %type expression_statement selection_statement iteration_statement %type jump_statement function_body else_statement string_literal_val %type critical_statement asm_statement label +%type generic_selection generic_assoc_list generic_association +%type implicit_block statements_and_implicit block_item_list %type designator designator_list designation designation_opt %type initializer initializer_list %type unary_operator assignment_operator struct_or_union @@ -139,158 +146,29 @@ bool uselessDecl = TRUE; %% -file - : /* empty */ - { - werror(W_EMPTY_SOURCE_FILE); - } - | program - ; - -program - : external_definition - | program external_definition - ; - -external_definition - : function_definition - { - // blockNo = 0; - } - | declaration - { - ignoreTypedefType = 0; - if ($1 && $1->type && IS_FUNC($1->type)) - { - /* The only legal storage classes for - * a function prototype (declaration) - * are extern and static. extern is the - * default. Thus, if this function isn't - * explicitly marked static, mark it - * extern. - */ - if ($1->etype && IS_SPEC($1->etype) && !SPEC_STAT($1->etype)) - { - SPEC_EXTR($1->etype) = 1; - } - } - addSymChain (&$1); - allocVariables ($1); - cleanUpLevel (SymbolTab, 1); - } - | addressmod - ; - -function_definition - : function_declarator - { /* function type not specified */ - /* assume it to be 'int' */ - addDecl($1,0,newIntLink()); - $1 = createFunctionDecl($1); - } - function_body { - $$ = createFunction($1,$3); - } - | declaration_specifiers function_declarator - { - pointerTypes($2->type,copyLinkChain($1)); - addDecl($2,0,$1); - $2 = createFunctionDecl($2); - } - function_body - { - $$ = createFunction($2,$4); - } - ; - -function_attribute - : function_attributes - | function_attributes function_attribute { $$ = mergeSpec($1,$2,"function_attribute"); } - ; - -function_attributes - : USING constant_expr { - $$ = newLink(SPECIFIER); - FUNC_REGBANK($$) = (int) ulFromVal(constExprValue($2,TRUE)); - } - | REENTRANT { $$ = newLink (SPECIFIER); - FUNC_ISREENT($$)=1; - } - | CRITICAL { $$ = newLink (SPECIFIER); - FUNC_ISCRITICAL($$) = 1; - } - | NAKED { $$ = newLink (SPECIFIER); - FUNC_ISNAKED($$)=1; - } - | JAVANATIVE { $$ = newLink (SPECIFIER); - FUNC_ISJAVANATIVE($$)=1; - } - | OVERLAY { $$ = newLink (SPECIFIER); - FUNC_ISOVERLAY($$)=1; - } - | NONBANKED {$$ = newLink (SPECIFIER); - FUNC_NONBANKED($$) = 1; - if (FUNC_BANKED($$)) { - werror(W_BANKED_WITH_NONBANKED); - } - } - | SHADOWREGS {$$ = newLink (SPECIFIER); - FUNC_ISSHADOWREGS($$) = 1; - } - | SD_WPARAM {$$ = newLink (SPECIFIER); - FUNC_ISWPARAM($$) = 1; - } - | BANKED {$$ = newLink (SPECIFIER); - FUNC_BANKED($$) = 1; - if (FUNC_NONBANKED($$)) { - werror(W_BANKED_WITH_NONBANKED); - } - } - | Interrupt_storage - { - $$ = newLink (SPECIFIER); - FUNC_INTNO($$) = $1; - FUNC_ISISR($$) = 1; - } - | SMALLC { $$ = newLink (SPECIFIER); - FUNC_ISSMALLC($$)=1; - } - ; - -function_body - : compound_statement - | declaration_list compound_statement - { - werror (E_OLD_STYLE, ($1 ? $1->name: "")); - exit (1); - } - ; - -offsetof_member_designator - : identifier { $$ = newAst_VALUE (symbolVal ($1)); } - | offsetof_member_designator '.' { ignoreTypedefType = 1; } identifier - { - ignoreTypedefType = 0; - $4 = newSymbol ($4->name, NestLevel); - $4->implicit = 1; - $$ = newNode ('.', $1, newAst_VALUE (symbolVal ($4))); - } - | offsetof_member_designator '[' expr ']' - { - $$ = newNode ('[', $1, $3); - } - ; + /* C2X A.2.1 Expressions */ primary_expr : identifier { $$ = newAst_VALUE (symbolVal ($1)); } | CONSTANT { $$ = newAst_VALUE ($1); } | string_literal_val | '(' expr ')' { $$ = $2; } + | generic_selection ; -string_literal_val - : STRING_LITERAL { $$ = newAst_VALUE (strVal ($1)); } - ; +generic_selection + : GENERIC '(' assignment_expr ',' generic_assoc_list ')' { $$ = newNode (GENERIC, $3, $5); } + ; + +generic_assoc_list + : generic_association { $$ = newNode (GENERIC_ASSOC_LIST, NULL, $1); } + | generic_assoc_list ',' generic_association { $$ = newNode (GENERIC_ASSOC_LIST, $1, $3); } + ; + +generic_association + : type_name ':' assignment_expr { $$ = newNode (GENERIC_ASSOCIATION, newAst_LINK($1), $3); } + | DEFAULT ':' assignment_expr { $$ = newNode (GENERIC_ASSOCIATION,NULL,$3); } + ; postfix_expr : primary_expr @@ -319,6 +197,22 @@ postfix_expr { $$ = newNode(INC_OP,$1,NULL);} | postfix_expr DEC_OP { $$ = newNode(DEC_OP,$1,NULL); } + | '(' type_name ')' '{' initializer_list '}' + { + /* if (!options.std_c99) */ + werror(E_COMPOUND_LITERALS_C99); + + /* TODO: implement compound literals (C99) */ + $$ = newAst_VALUE (valueFromLit (0)); + } + | '(' type_name ')' '{' initializer_list ',' '}' + { + /* if (!options.std_c99) */ + werror(E_COMPOUND_LITERALS_C99); + + /* TODO: implement compound literals (C99) */ + $$ = newAst_VALUE (valueFromLit (0)); + } ; argument_expr_list @@ -330,7 +224,15 @@ unary_expr : postfix_expr | INC_OP unary_expr { $$ = newNode (INC_OP, NULL, $2); } | DEC_OP unary_expr { $$ = newNode (DEC_OP, NULL, $2); } - | unary_operator cast_expr { $$ = newNode ($1, $2, NULL); } + | unary_operator cast_expr + { + if ($1 == '&' && IS_AST_OP ($2) && $2->opval.op == '*' && $2->right == NULL) + $$ = $2->left; + else if ($1 == '*' && IS_AST_OP ($2) && $2->opval.op == '&' && $2->right == NULL) + $$ = $2->left; + else + $$ = newNode ($1, $2, NULL); + } | SIZEOF unary_expr { $$ = newNode (SIZEOF, NULL, $2); } | SIZEOF '(' type_name ')' { $$ = newAst_VALUE (sizeofOp ($3)); } | ALIGNOF '(' type_name ')'{ $$ = newAst_VALUE (alignofOp ($3)); } @@ -414,7 +316,7 @@ logical_or_expr conditional_expr : logical_or_expr - | logical_or_expr '?' { seqPointNo++;} logical_or_expr ':' conditional_expr + | logical_or_expr '?' { seqPointNo++;} expr ':' conditional_expr { $$ = newNode(':',$4,$6); $$ = newNode('?',$1,$$); @@ -423,7 +325,7 @@ conditional_expr assignment_expr : conditional_expr - | cast_expr assignment_operator assignment_expr + | unary_expr assignment_operator assignment_expr { switch ($2) { @@ -486,10 +388,17 @@ expr | expr ',' { seqPointNo++;} assignment_expr { $$ = newNode(',',$1,$4);} ; +expr_opt + : { $$ = NULL; seqPointNo++; } + | expr { $$ = $1; seqPointNo++; } + ; + constant_expr : conditional_expr ; + /* C2X A.2.2 Declarations */ + declaration : declaration_specifiers ';' { @@ -499,7 +408,7 @@ declaration { structdef *sdef = SPEC_STRUCT($1); structdef *osdef; - osdef = findSymWithBlock (StructTab, sdef->tagsym, currBlockno); + osdef = findSymWithBlock (StructTab, sdef->tagsym, currBlockno, NestLevel); if (osdef && osdef->block != currBlockno) { sdef = newStruct(osdef->tagsym->name); @@ -522,6 +431,26 @@ declaration for (sym1 = sym = reverseSyms($2);sym != NULL;sym = sym->next) { sym_link *lnk = copyLinkChain($1); + sym_link *l0 = NULL, *l1 = NULL, *l2 = NULL; + /* check illegal declaration */ + for (l0 = sym->type; l0 != NULL; l0 = l0->next) + if (IS_PTR (l0)) + break; + /* check if creating intances of structs with flexible arrays */ + for (l1 = lnk; l1 != NULL; l1 = l1->next) + if (IS_STRUCT (l1) && SPEC_STRUCT (l1)->b_flexArrayMember) + break; + if (!options.std_c99 && l0 == NULL && l1 != NULL && SPEC_EXTR($1) != 1) + werror (W_FLEXARRAY_INSTRUCT, sym->name); + /* check if creating intances of function type */ + for (l1 = lnk; l1 != NULL; l1 = l1->next) + if (IS_FUNC (l1)) + break; + for (l2 = lnk; l2 != NULL; l2 = l2->next) + if (IS_PTR (l2)) + break; + if (l0 == NULL && l2 == NULL && l1 != NULL) + werrorfl(sym->fileDef, sym->lineDef, E_TYPE_IS_FUNCTION, sym->name); /* do the pointer stuff */ pointerTypes(sym->type,lnk); addDecl (sym,0,lnk); @@ -530,7 +459,11 @@ declaration uselessDecl = TRUE; $$ = sym1; } - | static_assert_declaration ';' + | static_assert_declaration + { + $$ = NULL; + } + | attribute_declaration { $$ = NULL; } @@ -545,11 +478,11 @@ declaration_specifiers_ /* find the spec and replace it */ $$ = mergeDeclSpec($1, $2, "storage_class_specifier declaration_specifiers - skipped"); } - | type_specifier { $$ = $1; } - | type_specifier declaration_specifiers_ { + | type_specifier_qualifier { $$ = $1; } + | type_specifier_qualifier declaration_specifiers_ { /* if the decl $2 is not a specifier */ /* find the spec and replace it */ - $$ = mergeDeclSpec($1, $2, "type_specifier declaration_specifiers - skipped"); + $$ = mergeDeclSpec($1, $2, "type_specifier_qualifier declaration_specifiers - skipped"); } | function_specifier { $$ = $1; } | function_specifier declaration_specifiers_ { @@ -557,12 +490,6 @@ declaration_specifiers_ /* find the spec and replace it */ $$ = mergeDeclSpec($1, $2, "function_specifier declaration_specifiers - skipped"); } - | alignment_specifier { $$ = $1; } - | alignment_specifier declaration_specifiers_ { - /* if the decl $2 is not a specifier */ - /* find the spec and replace it */ - $$ = mergeDeclSpec($1, $2, "alignment_specifier declaration_specifiers - skipped"); - } ; init_declarator_list @@ -575,44 +502,8 @@ init_declarator | declarator '=' initializer { $1->ival = $3; seqPointNo++; } ; -designation_opt - : { $$ = NULL; } - | designation - ; - -designation - : designator_list '=' { $$ = revDesignation($1); } - ; - -designator_list - : designator - | designator_list designator { $2->next = $1; $$ = $2; } - ; - -designator - : '[' constant_expr ']' - { - value *tval; - int elemno; - - tval = constExprValue($2, TRUE); - /* if it is not a constant then Error */ - if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) - { - werror (E_CONST_EXPECTED); - elemno = 0; /* arbitrary fixup */ - } - else - { - if ((elemno = (int) ulFromVal(tval)) < 0) - { - werror (E_BAD_DESIGNATOR); - elemno = 0; /* arbitrary fixup */ - } - } - $$ = newDesignation(DESIGNATOR_ARRAY, &elemno); - } - | '.' identifier { $$ = newDesignation(DESIGNATOR_STRUCT,$2); } +attribute_declaration + : attribute_specifier_sequence ';' ; storage_class_specifier @@ -628,6 +519,11 @@ storage_class_specifier $$ = newLink (SPECIFIER); SPEC_STAT($$) = 1; } + | THREAD_LOCAL + { + $$ = 0; + werror(E_THREAD_LOCAL); + } | AUTO { $$ = newLink (SPECIFIER); SPEC_SCLS($$) = S_AUTO; @@ -638,56 +534,10 @@ storage_class_specifier } ; -function_specifier - : INLINE { - $$ = newLink (SPECIFIER); - SPEC_INLINE($$) = 1; - } - | NORETURN { - $$ = newLink (SPECIFIER); - SPEC_NORETURN($$) = 1; - } - ; - -alignment_specifier - : ALIGNAS '(' type_name ')' - { - checkTypeSanity ($3, "(_Alignas)"); - $$ = newLink (SPECIFIER); - SPEC_ALIGNAS($$) = 1; - } - | ALIGNAS '(' constant_expr ')' - { - value *val = constExprValue ($3, TRUE); - $$ = newLink (SPECIFIER); - SPEC_ALIGNAS($$) = 0; - if (!val) - werror (E_CONST_EXPECTED); - else if (ulFromVal (val) == 0 || isPowerOf2 (ulFromVal (val)) && ulFromVal (val) <= port->mem.maxextalign) - SPEC_ALIGNAS($$) = ulFromVal(val); - else - werror (E_ALIGNAS, ulFromVal(val)); - } - ; - -Interrupt_storage - : INTERRUPT { $$ = INTNO_UNSPEC; } - | INTERRUPT constant_expr - { int intno = (int) ulFromVal(constExprValue($2,TRUE)); - if ((intno >= 0) && (intno <= INTNO_MAX)) - $$ = intno; - else - { - werror(E_INT_BAD_INTNO, intno); - $$ = INTNO_UNSPEC; - } - } - ; - type_specifier - : SD_BOOL { + : SD_VOID { $$=newLink(SPECIFIER); - SPEC_NOUN($$) = V_BOOL; + SPEC_NOUN($$) = V_VOID; ignoreTypedefType = 1; } | SD_CHAR { @@ -704,12 +554,17 @@ type_specifier $$=newLink(SPECIFIER); SPEC_NOUN($$) = V_INT; ignoreTypedefType = 1; - } + } | SD_LONG { $$=newLink(SPECIFIER); SPEC_LONG($$) = 1; ignoreTypedefType = 1; } + | SD_FLOAT { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_FLOAT; + ignoreTypedefType = 1; + } | SIGNED { $$=newLink(SPECIFIER); $$->select.s.b_signed = 1; @@ -720,61 +575,36 @@ type_specifier SPEC_USIGN($$) = 1; ignoreTypedefType = 1; } - | SD_VOID { + | SD_BOOL { $$=newLink(SPECIFIER); - SPEC_NOUN($$) = V_VOID; - ignoreTypedefType = 1; - } - | SD_CONST { - $$=newLink(SPECIFIER); - SPEC_CONST($$) = 1; - } - | VOLATILE { - $$=newLink(SPECIFIER); - SPEC_VOLATILE($$) = 1; - } - | RESTRICT { - $$=newLink(SPECIFIER); - SPEC_RESTRICT($$) = 1; - } - | ADDRSPACE_NAME { - $$=newLink(SPECIFIER); - SPEC_ADDRSPACE($$) = findSym (AddrspaceTab, 0, $1); - } - | SD_FLOAT { - $$=newLink(SPECIFIER); - SPEC_NOUN($$) = V_FLOAT; + SPEC_NOUN($$) = V_BOOL; ignoreTypedefType = 1; } + | struct_or_union_specifier { + uselessDecl = FALSE; + $$ = $1; + ignoreTypedefType = 1; + } + | enum_specifier { + cenum = NULL; + uselessDecl = FALSE; + ignoreTypedefType = 1; + $$ = $1; + } + | TYPE_NAME + { + symbol *sym; + sym_link *p; + sym = findSym(TypedefTab,NULL,$1); + $$ = p = copyLinkChain(sym ? sym->type : NULL); + SPEC_TYPEDEF(getSpec(p)) = 0; + ignoreTypedefType = 1; + } | FIXED16X16 { $$=newLink(SPECIFIER); SPEC_NOUN($$) = V_FIXED16X16; ignoreTypedefType = 1; } - | XDATA { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_XDATA; - } - | CODE { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_CODE; - } - | EEPROM { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_EEPROM; - } - | DATA { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_DATA; - } - | IDATA { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_IDATA; - } - | PDATA { - $$ = newLink (SPECIFIER); - SPEC_SCLS($$) = S_PDATA; - } | BIT { $$=newLink(SPECIFIER); SPEC_NOUN($$) = V_BIT; @@ -790,96 +620,23 @@ type_specifier /* now get the abs addr from value */ SPEC_ADDR($$) = (unsigned int) ulFromVal(constExprValue($2,TRUE)); } - | struct_or_union_specifier { - uselessDecl = FALSE; - $$ = $1; - ignoreTypedefType = 1; - } - | enum_specifier { - cenum = NULL; - uselessDecl = FALSE; - ignoreTypedefType = 1; - $$ = $1; - } - | TYPE_NAME - { - symbol *sym; - sym_link *p; - sym = findSym(TypedefTab,NULL,$1); - $$ = p = copyLinkChain(sym ? sym->type : NULL); - SPEC_TYPEDEF(getSpec(p)) = 0; - ignoreTypedefType = 1; - } - | sfr_reg_bit - ; - -sfr_reg_bit - : SBIT { - $$ = newLink(SPECIFIER); - SPEC_NOUN($$) = V_SBIT; - SPEC_SCLS($$) = S_SBIT; - SPEC_BLEN($$) = 1; - SPEC_BSTR($$) = 0; - ignoreTypedefType = 1; - } - | sfr_attributes - ; - -sfr_attributes - : SFR { - $$ = newLink(SPECIFIER); - FUNC_REGBANK($$) = 0; - SPEC_NOUN($$) = V_CHAR; - SPEC_SCLS($$) = S_SFR; - SPEC_USIGN($$) = 1; - ignoreTypedefType = 1; - } - | SFR BANKED { - $$ = newLink(SPECIFIER); - FUNC_REGBANK($$) = 1; - SPEC_NOUN($$) = V_CHAR; - SPEC_SCLS($$) = S_SFR; - SPEC_USIGN($$) = 1; - ignoreTypedefType = 1; - } - ; -sfr_attributes - : SFR16 { - $$ = newLink(SPECIFIER); - FUNC_REGBANK($$) = 0; - SPEC_NOUN($$) = V_INT; - SPEC_SCLS($$) = S_SFR; - SPEC_USIGN($$) = 1; - ignoreTypedefType = 1; - } - ; -sfr_attributes - : SFR32 { - $$ = newLink(SPECIFIER); - FUNC_REGBANK($$) = 0; - SPEC_NOUN($$) = V_INT; - SPEC_SCLS($$) = S_SFR; - SPEC_LONG($$) = 1; - SPEC_USIGN($$) = 1; - ignoreTypedefType = 1; - } - ; + | sfr_reg_bit; struct_or_union_specifier - : struct_or_union opt_stag + : struct_or_union attribute_specifier_sequence_opt opt_stag { structdef *sdef; - if (! $2->tagsym) + if (! $3->tagsym) { /* no tag given, so new struct def for current scope */ - addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + addSym (StructTab, $3, $3->tag, $3->level, currBlockno, 0); } else { - sdef = findSymWithBlock (StructTab, $2->tagsym, currBlockno); + sdef = findSymWithBlock (StructTab, $3->tagsym, currBlockno, NestLevel); if (sdef) { /* Error if a complete type already defined in this scope */ @@ -887,46 +644,46 @@ struct_or_union_specifier { if (sdef->fields) { - werror(E_STRUCT_REDEF, $2->tag); + werror(E_STRUCT_REDEF, $3->tag); werrorfl(sdef->tagsym->fileDef, sdef->tagsym->lineDef, E_PREVIOUS_DEF); } else { - $2 = sdef; /* We are completing an incomplete type */ + $3 = sdef; /* We are completing an incomplete type */ } } else { /* There is an existing struct def in an outer scope. */ /* Create new struct def for current scope */ - addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + addSym (StructTab, $3, $3->tag, $3->level, currBlockno, 0); } } else { /* There is no existing struct def at all. */ /* Create new struct def for current scope */ - addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + addSym (StructTab, $3, $3->tag, $3->level, currBlockno, 0); } } - if (!$2->type) + if (!$3->type) { - $2->type = $1; + $3->type = $1; } else { - if ($2->type != $1) - werror(E_BAD_TAG, $2->tag, $1==STRUCT ? "struct" : "union"); + if ($3->type != $1) + werror(E_BAD_TAG, $3->tag, $1==STRUCT ? "struct" : "union"); } } - '{' struct_declaration_list '}' + '{' member_declaration_list '}' { structdef *sdef; symbol *sym, *dsym; // check for errors in structure members - for (sym=$5; sym; sym=sym->next) + for (sym=$6; sym; sym=sym->next) { if (IS_ABSOLUTE(sym->etype)) { @@ -951,8 +708,8 @@ struct_or_union_specifier } /* Create a structdef */ - sdef = $2; - sdef->fields = reverseSyms($5); /* link the fields */ + sdef = $3; + sdef->fields = reverseSyms($6); /* link the fields */ sdef->size = compStructSize($1, sdef); /* update size of */ promoteAnonStructs ($1, sdef); @@ -961,30 +718,31 @@ struct_or_union_specifier SPEC_NOUN($$) = V_STRUCT; SPEC_STRUCT($$)= sdef; } - | struct_or_union stag + | struct_or_union attribute_specifier_sequence_opt stag { structdef *sdef; - sdef = findSymWithBlock (StructTab, $2->tagsym, currBlockno); + sdef = findSymWithBlock (StructTab, $3->tagsym, currBlockno, NestLevel); + if (sdef) - $2 = sdef; + $3 = sdef; else { /* new struct def for current scope */ - addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + addSym (StructTab, $3, $3->tag, $3->level, currBlockno, 0); } $$ = newLink(SPECIFIER); SPEC_NOUN($$) = V_STRUCT; - SPEC_STRUCT($$) = $2; + SPEC_STRUCT($$) = $3; - if (!$2->type) + if (!$3->type) { - $2->type = $1; + $3->type = $1; } else { - if ($2->type != $1) - werror(E_BAD_TAG, $2->tag, $1==STRUCT ? "struct" : "union"); + if ($3->type != $1) + werror(E_BAD_TAG, $3->tag, $1==STRUCT ? "struct" : "union"); } } ; @@ -994,40 +752,9 @@ struct_or_union | UNION { $$ = UNION; ignoreTypedefType = 1; } ; -opt_stag - : stag - | { /* synthesize a name add to structtable */ - ignoreTypedefType = 0; - $$ = newStruct(genSymName(NestLevel)); - $$->level = NestLevel; - $$->block = currBlockno; - $$->tagsym = NULL; - //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); - } - ; - -stag - : identifier - { /* add name to structure table */ - ignoreTypedefType = 0; - $$ = newStruct($1->name); - $$->level = NestLevel; - $$->block = currBlockno; - $$->tagsym = $1; - //$$ = findSymWithBlock (StructTab, $1, currBlockno); - //if (! $$ ) - // { - // $$ = newStruct($1->name); - // $$->level = NestLevel; - // $$->tagsym = $1; - // //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); - // } - } - ; - -struct_declaration_list - : struct_declaration - | struct_declaration_list struct_declaration +member_declaration_list + : member_declaration + | member_declaration_list member_declaration { symbol *sym = $2; @@ -1039,14 +766,14 @@ struct_declaration_list } ; -struct_declaration - : type_specifier_list struct_declarator_list ';' +member_declaration + : attribute_specifier_sequence_opt specifier_qualifier_list member_declarator_list ';' { /* add this type to all the symbols */ symbol *sym; - for ( sym = $2; sym != NULL; sym = sym->next ) + for ( sym = $3; sym != NULL; sym = sym->next ) { - sym_link *btype = copyLinkChain($1); + sym_link *btype = copyLinkChain($2); pointerTypes(sym->type, btype); if (!sym->type) @@ -1060,31 +787,32 @@ struct_declaration checkTypeSanity(sym->etype, sym->name); } ignoreTypedefType = 0; - $$ = $2; + $$ = $3; } ; -struct_declarator_list - : struct_declarator - | struct_declarator_list ',' struct_declarator +type_specifier_qualifier + : type_specifier { $$ = $1; } + | type_qualifier { $$ = $1; } + | alignment_specifier { $$ = $1; } + ; + +member_declarator_list + : member_declarator + | member_declarator_list ',' member_declarator { $3->next = $1; $$ = $3; } ; -struct_declarator +member_declarator : declarator | ':' constant_expr { unsigned int bitsize; $$ = newSymbol (genSymName(NestLevel), NestLevel); bitsize = (unsigned int) ulFromVal(constExprValue($2, TRUE)); - if (bitsize > (port->s.int_size * 8)) - { - bitsize = port->s.int_size * 8; - werror(E_BITFLD_SIZE, bitsize); - } if (!bitsize) bitsize = BITVAR_PAD; $$->bitVar = bitsize; @@ -1094,11 +822,7 @@ struct_declarator { unsigned int bitsize; bitsize = (unsigned int) ulFromVal(constExprValue($3, TRUE)); - if (bitsize > (port->s.int_size * 8)) - { - bitsize = port->s.int_size * 8; - werror(E_BITFLD_SIZE, bitsize); - } + if (!bitsize) { $$ = newSymbol (genSymName(NestLevel), NestLevel); @@ -1117,8 +841,38 @@ enum_specifier $$ = newEnumType ($3); SPEC_SCLS(getSpec($$)) = 0; } - | ENUM identifier '{' enumerator_list '}' + | ENUM '{' enumerator_list ',' '}' + { + if (!options.std_c99) + werror (E_ENUM_COMMA_C99); + $$ = newEnumType ($3); + SPEC_SCLS(getSpec($$)) = 0; + } + | ENUM identifier '{' enumerator_list '}' + { + symbol *csym; + sym_link *enumtype; + + csym = findSymWithLevel(enumTab, $2); + if ((csym && csym->level == $2->level)) + { + werrorfl($2->fileDef, $2->lineDef, E_DUPLICATE_TYPEDEF, csym->name); + werrorfl(csym->fileDef, csym->lineDef, E_PREVIOUS_DEF); + } + + enumtype = newEnumType ($4); + SPEC_SCLS(getSpec(enumtype)) = 0; + $2->type = enumtype; + + /* add this to the enumerator table */ + if (!csym) + addSym (enumTab, $2, $2->name, $2->level, $2->block, 0); + $$ = copyLinkChain(enumtype); + } + | ENUM identifier '{' enumerator_list ',' '}' { + if (!options.std_c99) + werror (E_ENUM_COMMA_C99); symbol *csym; sym_link *enumtype; @@ -1155,7 +909,6 @@ enum_specifier enumerator_list : enumerator - | enumerator_list ',' | enumerator_list ',' enumerator { $3->next = $1; @@ -1164,7 +917,7 @@ enumerator_list ; enumerator - : identifier opt_assign_expr + : identifier attribute_specifier_sequence_opt opt_assign_expr { symbol *sym; @@ -1174,7 +927,7 @@ enumerator werrorfl ($1->fileDef, $1->lineDef, E_DUPLICATE_MEMBER, "enum", $1->name); werrorfl (sym->fileDef, sym->lineDef, E_PREVIOUS_DEF); } - $1->type = copyLinkChain ($2->type); + $1->type = copyLinkChain ($3->type); $1->etype = getSpec ($1->type); SPEC_ENUM ($1->etype) = 1; $$ = $1; @@ -1183,93 +936,105 @@ enumerator } ; -opt_assign_expr - : '=' constant_expr - { - value *val; +type_qualifier + : SD_CONST { + $$=newLink(SPECIFIER); + SPEC_CONST($$) = 1; + } + | RESTRICT { + $$=newLink(SPECIFIER); + SPEC_RESTRICT($$) = 1; + } + | VOLATILE { + $$=newLink(SPECIFIER); + SPEC_VOLATILE($$) = 1; + } + | ADDRSPACE_NAME { + $$=newLink(SPECIFIER); + SPEC_ADDRSPACE($$) = findSym (AddrspaceTab, 0, $1); + } + | XDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_XDATA; + } + | CODE { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_CODE; + } + | EEPROM { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_EEPROM; + } + | DATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_DATA; + } + | IDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_IDATA; + } + | PDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_PDATA; + } + ; - val = constExprValue($2, TRUE); - if (!IS_INT(val->type) && !IS_CHAR(val->type) && !IS_BOOL(val->type)) - { - werror(E_ENUM_NON_INTEGER); - SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(val)); - val = constVal(lbuff); - } - $$ = cenum = val; - } - | { - if (cenum) - { - SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(cenum)+1); - $$ = cenum = constVal(lbuff); - } - else - { - $$ = cenum = constCharVal(0); - } - } +function_specifier + : INLINE { + $$ = newLink (SPECIFIER); + SPEC_INLINE($$) = 1; + } + | NORETURN { + $$ = newLink (SPECIFIER); + SPEC_NORETURN($$) = 1; + } ; -declarator - : declarator3 { $$ = $1; } - | pointer declarator3 - { - addDecl ($2,0,reverseLink($1)); - $$ = $2; - } - ; - -declarator3 - : declarator2_function_attributes { $$ = $1; } - | declarator2 { $$ = $1; } +alignment_specifier + : ALIGNAS '(' type_name ')' + { + checkTypeSanity ($3, "(_Alignas)"); + $$ = newLink (SPECIFIER); + SPEC_ALIGNAS($$) = 1; + } + | ALIGNAS '(' constant_expr ')' + { + value *val = constExprValue ($3, TRUE); + $$ = newLink (SPECIFIER); + SPEC_ALIGNAS($$) = 0; + if (!val) + werror (E_CONST_EXPECTED); + else if (ulFromVal (val) == 0 || isPowerOf2 (ulFromVal (val)) && ulFromVal (val) <= port->mem.maxextalign) + SPEC_ALIGNAS($$) = ulFromVal(val); + else + werror (E_ALIGNAS, ulFromVal(val)); + } ; -function_declarator - : declarator2_function_attributes { $$ = $1; } - | pointer declarator2_function_attributes +declarator + : direct_declarator { $$ = $1; } + | pointer direct_declarator { addDecl ($2,0,reverseLink($1)); $$ = $2; } ; -declarator2_function_attributes - : function_declarator2 { $$ = $1; } - | function_declarator2 function_attribute { - // copy the functionAttributes (not the args and hasVargs !!) - struct value *args; - unsigned hasVargs; - sym_link *funcType=$1->type; - - while (funcType && !IS_FUNC(funcType)) - funcType = funcType->next; - - if (!funcType) - werror (E_FUNC_ATTR); - else - { - args=FUNC_ARGS(funcType); - hasVargs=FUNC_HASVARARGS(funcType); - - memcpy (&funcType->funcAttrs, &$2->funcAttrs, - sizeof($2->funcAttrs)); - - FUNC_ARGS(funcType)=args; - FUNC_HASVARARGS(funcType)=hasVargs; - - // just to be sure - memset (&$2->funcAttrs, 0, - sizeof($2->funcAttrs)); - - addDecl ($1,0,$2); - } - } +direct_declarator + : identifier + | '(' declarator ')' { $$ = $2; } + | array_declarator + | declarator2_function_attributes ; declarator2 : identifier | '(' declarator ')' { $$ = $2; } - | declarator3 '[' ']' + | array_declarator + ; + +array_declarator: + direct_declarator '[' ']' { sym_link *p; @@ -1278,7 +1043,26 @@ declarator2 DCL_ELEM(p) = 0; addDecl($1,0,p); } - | declarator3 '[' constant_expr ']' + | direct_declarator '[' type_qualifier_list ']' + { + sym_link *p, *n; + + if (!options.std_c99) + werror (E_QUALIFIED_ARRAY_PARAM_C99); + + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + DCL_ELEM(p) = 0; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1,0,p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | direct_declarator '[' constant_expr ']' { sym_link *p; value *tval; @@ -1307,16 +1091,207 @@ declarator2 DCL_ELEM(p) = size; addDecl($1, 0, p); } + | direct_declarator '[' STATIC constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + werror (E_STATIC_ARRAY_PARAM_C99); + + tval = constExprValue($4, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | direct_declarator '[' type_qualifier_list constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + werror (E_QUALIFIED_ARRAY_PARAM_C99); + + tval = constExprValue($4, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | direct_declarator '[' STATIC type_qualifier_list constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + { + werror (E_STATIC_ARRAY_PARAM_C99); + werror (E_QUALIFIED_ARRAY_PARAM_C99); + } + + tval = constExprValue($5, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($4); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($4); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($4); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($4); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | direct_declarator '[' type_qualifier_list STATIC constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + { + werror (E_QUALIFIED_ARRAY_PARAM_C99); + werror (E_STATIC_ARRAY_PARAM_C99); + } + + tval = constExprValue($5, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + ; + +declarator2_function_attributes + : function_declarator { $$ = $1; } + | function_declarator function_attribute { + // copy the functionAttributes (not the args and hasVargs !!) + struct value *args; + unsigned hasVargs; + sym_link *funcType=$1->type; + + while (funcType && !IS_FUNC(funcType)) + funcType = funcType->next; + + if (!funcType) + werror (E_FUNC_ATTR); + else + { + args=FUNC_ARGS(funcType); + hasVargs=FUNC_HASVARARGS(funcType); + + memcpy (&funcType->funcAttrs, &$2->funcAttrs, + sizeof($2->funcAttrs)); + + FUNC_ARGS(funcType)=args; + FUNC_HASVARARGS(funcType)=hasVargs; + + // just to be sure + memset (&$2->funcAttrs, 0, + sizeof($2->funcAttrs)); + + addDecl ($1,0,$2); + } + } ; -function_declarator2 +function_declarator : declarator2 '(' ')' { addDecl ($1, FUNCTION, NULL); } | declarator2 '(' { - NestLevel++; + NestLevel += LEVEL_UNIT; STACK_PUSH(blockNum, currBlockno); btree_add_child(currBlockno, ++blockNo); currBlockno = blockNo; @@ -1338,28 +1313,39 @@ function_declarator2 FUNC_ARGS(funcType) = reverseVal($4); /* nest level was incremented to take care of the parms */ - NestLevel--; + NestLevel -= LEVEL_UNIT; currBlockno = STACK_POP(blockNum); seqPointNo++; /* not a true sequence point, but helps resolve scope */ // if this was a pointer (to a function) if (!IS_FUNC($1->type)) - cleanUpLevel(SymbolTab, NestLevel + 1); + cleanUpLevel(SymbolTab, NestLevel + LEVEL_UNIT); $$ = $1; } | declarator2 '(' identifier_list ')' { werror(E_OLD_STYLE,$1->name); - /* assume it returns an int */ - $1->type = $1->etype = newIntLink(); + + addDecl ($1, FUNCTION, NULL); + $$ = $1; } ; pointer : unqualified_pointer { $$ = $1;} - | unqualified_pointer type_specifier_list + | unqualified_pointer AT constant_expr /* Special case to allow __at at the end */ + { + sym_link *n = newLink(SPECIFIER); + /* add this to the storage class specifier */ + SPEC_ABSA(n) = 1; /* set the absolute addr flag */ + /* now get the abs addr from value */ + SPEC_ADDR(n) = (unsigned int) ulFromVal(constExprValue($3,TRUE)); + n->next = $1; + $$ = n; + } + | unqualified_pointer type_qualifier_list { $$ = $1; if (IS_SPEC($2)) { @@ -1372,13 +1358,33 @@ pointer else werror (W_PTR_TYPE_INVALID); } + | unqualified_pointer type_qualifier_list AT constant_expr /* Special case to allow __at at the end */ + { + if (IS_SPEC($2)) { + DCL_TSPEC($1) = $2; + DCL_PTR_CONST($1) = SPEC_CONST($2); + DCL_PTR_VOLATILE($1) = SPEC_VOLATILE($2); + DCL_PTR_RESTRICT($1) = SPEC_RESTRICT($2); + DCL_PTR_ADDRSPACE($1) = SPEC_ADDRSPACE($2); + } + else + werror (W_PTR_TYPE_INVALID); + + sym_link *n = newLink(SPECIFIER); + /* add this to the storage class specifier */ + SPEC_ABSA(n) = 1; /* set the absolute addr flag */ + /* now get the abs addr from value */ + SPEC_ADDR(n) = (unsigned int) ulFromVal(constExprValue($4,TRUE)); + n->next = $1; + $$ = n; + } | unqualified_pointer pointer { $$ = $1; $$->next = $2; DCL_TYPE($2)=port->unqualified_pointer; } - | unqualified_pointer type_specifier_list pointer + | unqualified_pointer type_qualifier_list pointer { $$ = $1; if (IS_SPEC($2) && DCL_TYPE($3) == UPOINTER) { @@ -1425,26 +1431,13 @@ unqualified_pointer } ; -type_specifier_list : type_specifier_list_ { $$ = finalizeSpec($1); }; - -type_specifier_list_ - : type_specifier - //| type_specifier_list_ type_specifier { $$ = mergeSpec ($1,$2, "type_specifier_list"); } - | type_specifier_list_ type_specifier { - /* if the decl $2 is not a specifier */ - /* find the spec and replace it */ - $$ = mergeDeclSpec($1, $2, "type_specifier_list type_specifier skipped"); - } - ; - -identifier_list - : identifier - | identifier_list ',' identifier - { - $3->next = $1; - $$ = $3; - } - ; +type_qualifier_list + : type_qualifier + | type_qualifier_list type_qualifier + { + $$ = mergeDeclSpec($1, $2, "type_qualifier_list type_qualifier skipped"); + } + ; parameter_type_list : parameter_list @@ -1470,6 +1463,8 @@ parameter_declaration werror (E_STORAGE_CLASS_FOR_PARAMETER, $2->name); } pointerTypes ($2->type, $1); + if (IS_SPEC ($2->etype)) + SPEC_NEEDSPAR($2->etype) = 0; addDecl ($2, 0, $1); for (loop = $2; loop; loop->_isparm = 1, loop = loop->next) ; @@ -1485,79 +1480,48 @@ parameter_declaration } ; -type_name - : declaration_specifiers - { - if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) - { - werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); - } - $$ = $1; ignoreTypedefType = 0; - } - | declaration_specifiers abstract_declarator - { - /* go to the end of the list */ - sym_link *p; - - if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) - { - werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); - } - pointerTypes ($2,$1); - for (p = $2; p && p->next; p = p->next) - ; - if (!p) - { - werror(E_SYNTAX_ERROR, yytext); - } - else - { - p->next = $1; - } - $$ = $2; - ignoreTypedefType = 0; - } - ; - abstract_declarator : pointer { $$ = reverseLink($1); } - | abstract_declarator2 - | pointer abstract_declarator2 { $1 = reverseLink($1); $1->next = $2; $$ = $1; + | direct_abstract_declarator + | pointer direct_abstract_declarator { $1 = reverseLink($1); $2->next = $1; $$ = $2; if (IS_PTR($1) && IS_FUNC($2)) DCL_TYPE($1) = CPOINTER; } ; -abstract_declarator2 +direct_abstract_declarator : '(' abstract_declarator ')' { $$ = $2; } - | '[' ']' { - $$ = newLink (DECLARATOR); - DCL_TYPE($$) = ARRAY; - DCL_ELEM($$) = 0; - } - | '[' constant_expr ']' { - value *val; - $$ = newLink (DECLARATOR); - DCL_TYPE($$) = ARRAY; - DCL_ELEM($$) = (int) ulFromVal(val = constExprValue($2,TRUE)); - } - | abstract_declarator2 '[' ']' { + | array_abstract_declarator + | function_abstract_declarator + ; + +direct_abstract_declarator_opt + : { $$ = NULL; } + | direct_abstract_declarator + ; + +array_abstract_declarator + : direct_abstract_declarator_opt '[' ']' { $$ = newLink (DECLARATOR); DCL_TYPE($$) = ARRAY; DCL_ELEM($$) = 0; - $$->next = $1; + if($1) + $$->next = $1; } - | abstract_declarator2 '[' constant_expr ']' + | direct_abstract_declarator_opt '[' constant_expr ']' { value *val; $$ = newLink (DECLARATOR); DCL_TYPE($$) = ARRAY; DCL_ELEM($$) = (int) ulFromVal(val = constExprValue($3,TRUE)); - $$->next = $1; + if($1) + $$->next = $1; } - | '(' ')' { $$ = NULL;} - | '(' parameter_type_list ')' { $$ = NULL;} - | abstract_declarator2 '(' ')' { + ; + +function_abstract_declarator + : '(' ')' { $$ = NULL;} + | direct_abstract_declarator '(' ')' { // $1 must be a pointer to a function sym_link *p=newLink(DECLARATOR); DCL_TYPE(p) = FUNCTION; @@ -1569,23 +1533,24 @@ abstract_declarator2 } $1->next=p; } - | abstract_declarator2 '(' + | '(' parameter_type_list ')' { $$ = NULL;} + | direct_abstract_declarator '(' { - NestLevel++; + NestLevel += LEVEL_UNIT; STACK_PUSH(blockNum, currBlockno); btree_add_child(currBlockno, ++blockNo); currBlockno = blockNo; } parameter_type_list ')' { - sym_link *p = newLink(DECLARATOR); + sym_link *p = newLink(DECLARATOR), *q; DCL_TYPE(p) = FUNCTION; FUNC_HASVARARGS(p) = IS_VARG($4); FUNC_ARGS(p) = reverseVal($4); /* nest level was incremented to take care of the parms */ - NestLevel--; + NestLevel -= LEVEL_UNIT; currBlockno = STACK_POP(blockNum); if (!$1) { @@ -1594,7 +1559,8 @@ abstract_declarator2 DCL_TYPE($1) = CPOINTER; $$ = $1; } - $1->next = p; + for (q = $1; q && q->next; q = q->next); + q->next = p; } ; @@ -1614,8 +1580,48 @@ initializer_list } ; +designation_opt + : { $$ = NULL; } + | designation + ; + +designation + : designator_list '=' { $$ = revDesignation($1); } + ; + +designator_list + : designator + | designator_list designator { $2->next = $1; $$ = $2; } + ; + +designator + : '[' constant_expr ']' + { + value *tval; + int elemno; + + tval = constExprValue($2, TRUE); + /* if it is not a constant then Error */ + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror (E_CONST_EXPECTED); + elemno = 0; /* arbitrary fixup */ + } + else + { + if ((elemno = (int) ulFromVal(tval)) < 0) + { + werror (E_BAD_DESIGNATOR); + elemno = 0; /* arbitrary fixup */ + } + } + $$ = newDesignation(DESIGNATOR_ARRAY, &elemno); + } + | '.' identifier { $$ = newDesignation(DESIGNATOR_STRUCT,$2); } + ; + static_assert_declaration - : STATIC_ASSERT '(' constant_expr ',' STRING_LITERAL ')' + : STATIC_ASSERT '(' constant_expr ',' STRING_LITERAL ')' ';' { value *val = constExprValue ($3, TRUE); if (!val) @@ -1623,48 +1629,106 @@ static_assert_declaration else if (!ulFromVal(val)) werror (W_STATIC_ASSERTION, $5); } + | STATIC_ASSERT '(' constant_expr ')' ';' + { + value *val = constExprValue ($3, TRUE); + if (!options.std_c2x) + werror (E_STATIC_ASSERTION_C2X); + if (!val) + werror (E_CONST_EXPECTED); + else if (!ulFromVal(val)) + werror (W_STATIC_ASSERTION_2); + } + ; + +attribute_specifier_sequence + : attribute_specifier_sequence attribute_specifier + | attribute_specifier + ; + +attribute_specifier_sequence_opt + : /* empty */ + | attribute_specifier_sequence + ; + +attribute_specifier + : '[' '[' attribute_list ']' ']' + { + if (!options.std_c2x) + werror(E_ATTRIBUTE_C2X); + } + ; + +attribute_list + : /* empty */ + | attribute + | attribute_list ',' + | attribute_list ',' attribute + ; + +attribute + : attribute_token + | attribute_token attribute_argument_clause + ; + +attribute_token + : identifier + | identifier ATTRIBCOLON identifier + ; + +attribute_argument_clause + : '(' ')' + | '(' balanced_token_sequence ')' + ; + +balanced_token_sequence + : balanced_token + | balanced_token_sequence balanced_token + ; + +balanced_token + : identifier + | STRING_LITERAL ; + /* C2X A.2.3 Statements */ + statement : labeled_statement - | compound_statement | expression_statement - | selection_statement - | iteration_statement - | jump_statement + | attribute_specifier_sequence_opt compound_statement + { + $$ = $2; + } + | attribute_specifier_sequence_opt selection_statement + { + $$ = $2; + } + | attribute_specifier_sequence_opt iteration_statement + { + $$ = $2; + } + | attribute_specifier_sequence_opt jump_statement + { + $$ = $2; + } | critical_statement | asm_statement ; -critical - : CRITICAL { - inCritical++; - STACK_PUSH(continueStack,NULL); - STACK_PUSH(breakStack,NULL); - $$ = NULL; - } - ; - -critical_statement - : critical statement { - STACK_POP(breakStack); - STACK_POP(continueStack); - inCritical--; - $$ = newNode(CRITICAL,$2,NULL); - } - ; - labeled_statement - : label statement { $$ = $1; $1->right = $2; } + : label statement { if ($1) {$$ = $1; $1->right = $2;} else $$ = newNode (BLOCK, NULL, NULL); } + | attribute_specifier_sequence label statement { if ($2) {$$ = $2; $2->right = $3;} else $$ = newNode (BLOCK, NULL, NULL); } | label '}' { /* Support a label without a statement at the end of a */ /* compound statement as a SDCC extension. Include the */ /* closing brace as part of the rule to avoid an */ /* unacceptably large number of shift/reduce conflicts */ /* and then reinsert it to be parsed a second time. */ + werror(W_LABEL_WITHOUT_STATEMENT); $$ = $1; yychar = '}'; - }; + } ; label @@ -1689,7 +1753,7 @@ label start_block : '{' { - NestLevel++; + NestLevel += LEVEL_UNIT; STACK_PUSH(blockNum, currBlockno); btree_add_child(currBlockno, ++blockNo); currBlockno = blockNo; @@ -1700,84 +1764,32 @@ start_block end_block : '}' { - NestLevel--; + NestLevel -= LEVEL_UNIT; currBlockno = STACK_POP(blockNum); } ; compound_statement : start_block end_block { $$ = createBlock(NULL, NULL); } - | start_block statement_list end_block { $$ = createBlock(NULL, $2); } - | start_block declaration_list end_block + | start_block block_item_list end_block { - $$ = createBlock($2, NULL); - cleanUpLevel(StructTab, NestLevel + 1); - } - | start_block - declaration_list statement_list - end_block - { - $$ = createBlock($2, $3); - cleanUpLevel(StructTab, NestLevel + 1); + $$ = $2; + cleanUpLevel(StructTab, NestLevel + LEVEL_UNIT); } | error ';' { $$ = NULL; } ; -declaration_list - : declaration - { - /* if this is typedef declare it immediately */ - if ( $1 && IS_TYPEDEF($1->etype)) { - allocVariables ($1); - $$ = NULL; - } - else - $$ = $1; - ignoreTypedefType = 0; - addSymChain(&$1); - } - - | declaration_list declaration - { - symbol *sym; - - /* if this is a typedef */ - if ($2 && IS_TYPEDEF($2->etype)) { - allocVariables ($2); - $$ = $1; - } - else { - /* get to the end of the previous decl */ - if ( $1 ) { - $$ = sym = $1; - while (sym->next) - sym = sym->next; - sym->next = $2; - } - else - $$ = $2; - } - ignoreTypedefType = 0; - addSymChain(&$2); - } - ; - -statement_list - : statement - | statement_list statement { $$ = newNode(NULLOP,$1,$2);} +block_item_list + : statements_and_implicit { $$ = createBlock(NULL, $1); } + | declaration_list { $$ = createBlock($1, NULL); } + | declaration_list statements_and_implicit { $$ = createBlock($1, $2); } ; expression_statement - : ';' { $$ = NULL;} - | expr ';' { $$ = $1; seqPointNo++;} - ; - -else_statement - : ELSE statement { $$ = $2; } - | { $$ = NULL; } + : expr_opt ';' + | attribute_specifier_sequence expr ';' { $$ = $2; seqPointNo++;} ; - selection_statement : IF '(' expr ')' { seqPointNo++;} statement else_statement { @@ -1812,53 +1824,6 @@ selection_statement } ; -while : WHILE { /* create and push the continue , break & body labels */ - static int Lblnum = 0; - /* continue */ - SNPRINTF (lbuff, sizeof(lbuff), "_whilecontinue_%d",Lblnum); - STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); - /* break */ - SNPRINTF (lbuff, sizeof(lbuff), "_whilebreak_%d",Lblnum); - STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); - /* body */ - SNPRINTF (lbuff, sizeof(lbuff), "_whilebody_%d",Lblnum++); - $$ = newSymbol(lbuff,NestLevel); - } - ; - -do : DO { /* create and push the continue , break & body Labels */ - static int Lblnum = 0; - - /* continue */ - SNPRINTF(lbuff, sizeof(lbuff), "_docontinue_%d",Lblnum); - STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); - /* break */ - SNPRINTF(lbuff, sizeof(lbuff), "_dobreak_%d",Lblnum); - STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); - /* do body */ - SNPRINTF(lbuff, sizeof(lbuff), "_dobody_%d",Lblnum++); - $$ = newSymbol (lbuff,NestLevel); - } - ; - -for : FOR { /* create & push continue, break & body labels */ - static int Lblnum = 0; - - /* continue */ - SNPRINTF(lbuff, sizeof(lbuff), "_forcontinue_%d",Lblnum); - STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); - /* break */ - SNPRINTF(lbuff, sizeof(lbuff), "_forbreak_%d",Lblnum); - STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); - /* body */ - SNPRINTF(lbuff, sizeof(lbuff), "_forbody_%d",Lblnum); - $$ = newSymbol(lbuff,NestLevel); - /* condition */ - SNPRINTF(lbuff, sizeof(lbuff), "_forcond_%d",Lblnum++); - STACK_PUSH(forStack,newSymbol(lbuff,NestLevel)); - } - ; - iteration_statement : while '(' expr ')' { seqPointNo++;} statement { @@ -1900,33 +1865,73 @@ iteration_statement $$->right = createLabel(AST_FOR($$,continueLabel),NULL); $$ = newNode(NULLOP,$$,createLabel(AST_FOR($$,falseLabel),NULL)); noLineno--; + + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); } -; + | for '(' declaration expr_opt ';' expr_opt ')' + { + if (!options.std_c99) + werror (E_FOR_INITAL_DECLARATION_C99); -expr_opt - : { $$ = NULL; seqPointNo++; } - | expr { $$ = $1; seqPointNo++; } - ; + if ( $3 && IS_TYPEDEF($3->etype)) + allocVariables ($3); + ignoreTypedefType = 0; + addSymChain(&$3); + } + statement + { -jump_statement - : GOTO identifier ';' { - $2->islbl = 1; - $$ = newAst_VALUE(symbolVal($2)); - $$ = newNode(GOTO,$$,NULL); - } - | CONTINUE ';' { - /* make sure continue is in context */ - if (STACK_EMPTY(continueStack) || STACK_PEEK(continueStack) == NULL) { - werror(E_BREAK_CONTEXT); - $$ = NULL; - } - else { - $$ = newAst_VALUE(symbolVal(STACK_PEEK(continueStack))); - $$ = newNode(GOTO,$$,NULL); - /* mark the continue label as referenced */ - STACK_PEEK(continueStack)->isref = 1; - } - } + noLineno++; + + $$ = newNode(FOR,$9,NULL); + AST_FOR($$,trueLabel) = $1; + AST_FOR($$,continueLabel) = STACK_POP(continueStack); + AST_FOR($$,falseLabel) = STACK_POP(breakStack); + AST_FOR($$,condLabel) = STACK_POP(forStack); + AST_FOR($$,initExpr) = 0; + AST_FOR($$,condExpr) = $4; + AST_FOR($$,loopExpr) = $6; + + /* This continue label is not at the correct location, */ + /* but we need to create it now for proper binding. The */ + /* code that handles the FOR node will move it to the */ + /* proper location inside the for loop. */ + if (AST_FOR($$,continueLabel)->isref) + $$->right = createLabel(AST_FOR($$,continueLabel),NULL); + $$ = createBlock($3, newNode(NULLOP,$$,createLabel(AST_FOR($$,falseLabel),NULL))); + cleanUpLevel(StructTab, NestLevel + LEVEL_UNIT); + noLineno--; + + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + } + ; + +jump_statement + : GOTO identifier ';' { + if (inCriticalBlock) { + werror(E_INVALID_CRITICAL); + $$ = NULL; + } else { + $2->islbl = 1; + $$ = newAst_VALUE(symbolVal($2)); + $$ = newNode(GOTO,$$,NULL); + } + } + | CONTINUE ';' { + /* make sure continue is in context */ + if (STACK_EMPTY(continueStack) || STACK_PEEK(continueStack) == NULL) { + werror(E_BREAK_CONTEXT); + $$ = NULL; + } + else { + $$ = newAst_VALUE(symbolVal(STACK_PEEK(continueStack))); + $$ = newNode(GOTO,$$,NULL); + /* mark the continue label as referenced */ + STACK_PEEK(continueStack)->isref = 1; + } + } | BREAK ';' { if (STACK_EMPTY(breakStack) || STACK_PEEK(breakStack) == NULL) { werror(E_BREAK_CONTEXT); @@ -1939,7 +1944,7 @@ jump_statement } | RETURN ';' { seqPointNo++; - if (inCritical) { + if (inCriticalBlock) { werror(E_INVALID_CRITICAL); $$ = NULL; } else { @@ -1948,7 +1953,7 @@ jump_statement } | RETURN expr ';' { seqPointNo++; - if (inCritical) { + if (inCriticalBlock) { werror(E_INVALID_CRITICAL); $$ = NULL; } else { @@ -1957,6 +1962,621 @@ jump_statement } ; + /* C2X A.2.4 External definitions */ + +translation_unit + : external_declaration + | translation_unit external_declaration + ; + +external_declaration + : function_definition + { + // blockNo = 0; + } + | declaration + { + ignoreTypedefType = 0; + if ($1 && $1->type && IS_FUNC($1->type)) + { + /* The only legal storage classes for + * a function prototype (declaration) + * are extern and static. extern is the + * default. Thus, if this function isn't + * explicitly marked static, mark it + * extern. + */ + if ($1->etype && IS_SPEC($1->etype) && !SPEC_STAT($1->etype)) + { + SPEC_EXTR($1->etype) = 1; + } + } + addSymChain (&$1); + allocVariables ($1); + cleanUpLevel (SymbolTab, 1); + } + | addressmod + { + /* These empty braces here are apparently required by some version of GNU bison on MS Windows. See bug #2858. */ + } + ; + +function_definition + : declarator + { /* function type not specified */ + /* assume it to be 'int' */ + addDecl($1,0,newIntLink()); + $1 = createFunctionDecl($1); + if ($1) + { + if (FUNC_ISCRITICAL ($1->type)) + inCriticalFunction = 1; + strncpy (function_name, $1->name, sizeof (function_name) - 4); + memset (function_name + sizeof (function_name) - 4, 0x00, 4); + } + } + function_body + { + $$ = createFunction($1,$3); + if ($1 && FUNC_ISCRITICAL ($1->type)) + inCriticalFunction = 0; + } + | declaration_specifiers declarator + { + sym_link *p = copyLinkChain($1); + pointerTypes($2->type,p); + addDecl($2,0,p); + $2 = createFunctionDecl($2); + if ($2) + { + if (FUNC_ISCRITICAL ($2->type)) + inCriticalFunction = 1; + // warn for loss of calling convention for inlined functions. + if (FUNC_ISINLINE ($2->type) && + ( FUNC_ISZ88DK_CALLEE ($2->type) || FUNC_ISZ88DK_FASTCALL ($2->type) || + FUNC_BANKED ($2->type) || FUNC_REGBANK ($2->type) || + FUNC_ISOVERLAY ($2->type) || FUNC_ISISR ($2->type) )) + werror (W_INLINE_FUNCATTR, $2->name); + strncpy (function_name, $2->name, sizeof (function_name) - 4); + memset (function_name + sizeof (function_name) - 4, 0x00, 4); + } + } + function_body + { + $$ = createFunction($2,$4); + if ($2 && FUNC_ISCRITICAL ($2->type)) + inCriticalFunction = 0; + } + ; + +function_body + : compound_statement + | declaration_list compound_statement + { + werror (E_OLD_STYLE, ($1 ? $1->name: "")); + exit (1); + } + ; + + /* SDCC-specific stuff */ + +file + : /* empty */ + { + werror(W_EMPTY_SOURCE_FILE); + } + | translation_unit + ; + + + + +function_attribute + : function_attributes + | function_attributes function_attribute { $$ = mergeSpec($1,$2,"function_attribute"); } + ; + +function_attributes + : USING constant_expr { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = (int) ulFromVal(constExprValue($2,TRUE)); + } + | REENTRANT { $$ = newLink (SPECIFIER); + FUNC_ISREENT($$)=1; + } + | CRITICAL { $$ = newLink (SPECIFIER); + FUNC_ISCRITICAL($$) = 1; + } + | NAKED { $$ = newLink (SPECIFIER); + FUNC_ISNAKED($$)=1; + } + | JAVANATIVE { $$ = newLink (SPECIFIER); + FUNC_ISJAVANATIVE($$)=1; + } + | OVERLAY { $$ = newLink (SPECIFIER); + FUNC_ISOVERLAY($$)=1; + } + | NONBANKED {$$ = newLink (SPECIFIER); + FUNC_NONBANKED($$) = 1; + if (FUNC_BANKED($$)) { + werror(W_BANKED_WITH_NONBANKED); + } + } + | SHADOWREGS {$$ = newLink (SPECIFIER); + FUNC_ISSHADOWREGS($$) = 1; + } + | SD_WPARAM {$$ = newLink (SPECIFIER); + FUNC_ISWPARAM($$) = 1; + } + | BANKED {$$ = newLink (SPECIFIER); + FUNC_BANKED($$) = 1; + if (FUNC_NONBANKED($$)) { + werror(W_BANKED_WITH_NONBANKED); + } + } + | Interrupt_storage + { + $$ = newLink (SPECIFIER); + FUNC_INTNO($$) = $1; + FUNC_ISISR($$) = 1; + } + | TRAP + { + $$ = newLink (SPECIFIER); + FUNC_INTNO($$) = INTNO_TRAP; + FUNC_ISISR($$) = 1; + } + | SMALLC { $$ = newLink (SPECIFIER); + FUNC_ISSMALLC($$) = 1; + } + | Z88DK_FASTCALL { $$ = newLink (SPECIFIER); + FUNC_ISZ88DK_FASTCALL($$) = 1; + } + | Z88DK_CALLEE { $$ = newLink (SPECIFIER); + FUNC_ISZ88DK_CALLEE($$) = 1; + } + | Z88DK_PARAMS_OFFSET '(' constant_expr ')' + { + value *offset_v = constExprValue ($3, TRUE); + int offset = 0; + $$ = newLink(SPECIFIER); + if ( offset_v ) + offset = ulFromVal(offset_v); + $$->funcAttrs.z88dk_params_offset = offset; + } + | Z88DK_SHORTCALL '(' constant_expr ',' constant_expr ')' + { + value *rst_v = constExprValue ($3, TRUE); + value *value_v = constExprValue ($5, TRUE); + int rst = -1, value = -1; + $$ = newLink(SPECIFIER); + + if ( rst_v ) + rst = ulFromVal(rst_v); + if ( value_v ) + value = ulFromVal(value_v); + + if ( rst < 0 || rst > 56 || ( rst % 8 ) ) + { + werror(E_SHORTCALL_INVALID_VALUE, "rst", rst); + } + if ( value < 0 || value > 0xfff ) + { + werror(E_SHORTCALL_INVALID_VALUE, "value", value); + } + $$->funcAttrs.z88dk_shortcall_rst = rst; + $$->funcAttrs.z88dk_shortcall_val = value; + FUNC_ISZ88DK_SHORTCALL($$) = 1; + } + | PRESERVES_REGS '(' identifier_list ')' + { + const struct symbol *regsym; + $$ = newLink (SPECIFIER); + + for(regsym = $3; regsym; regsym = regsym->next) + { + int regnum; + + if (!port->getRegByName || ((regnum = port->getRegByName(regsym->name)) < 0)) + werror (W_UNKNOWN_REG, regsym->name); + else + $$->funcAttrs.preserved_regs[regnum] = TRUE; + } + } + ; + +offsetof_member_designator + : identifier { $$ = newAst_VALUE (symbolVal ($1)); } + | offsetof_member_designator '.' { ignoreTypedefType = 1; } identifier + { + ignoreTypedefType = 0; + $4 = newSymbol ($4->name, NestLevel); + $4->implicit = 1; + $$ = newNode ('.', $1, newAst_VALUE (symbolVal ($4))); + } + | offsetof_member_designator '[' expr ']' + { + $$ = newNode ('[', $1, $3); + } + ; + +string_literal_val + : STRING_LITERAL { + int cnt = 1; + int max = 253, min = 1; + char fb[256]; + /* refer to support/cpp/libcpp/macro.c for details */ + while ((((int) ($1[cnt] & 0xff)) & 0xff) == 0xff) + cnt++; + + if (cnt <= max) + { + $$ = newAst_VALUE (strVal ($1)); + } + else + { + memset (fb, 0x00, sizeof (fb)); + fb[0] = '"'; + strncpy (fb + 1, function_name, max - min + 1); + fb[max + 1] = '"'; + fb[max + 2] = 0; + fb[strlen (fb)] = '"'; + fb[strlen (fb) + 1] = 0; + $$ = newAst_VALUE (strVal (fb)); + } + } + ; + +Interrupt_storage + : INTERRUPT { $$ = INTNO_UNSPEC; } + | INTERRUPT constant_expr + { + value *val = constExprValue($2,TRUE); + int intno = (int) ulFromVal(val); + if (val && (intno >= 0) && (intno <= INTNO_MAX)) + $$ = intno; + else + { + werror(E_INT_BAD_INTNO, intno); + $$ = INTNO_UNSPEC; + } + } + ; + +sfr_reg_bit + : SBIT { + $$ = newLink(SPECIFIER); + SPEC_NOUN($$) = V_SBIT; + SPEC_SCLS($$) = S_SBIT; + SPEC_BLEN($$) = 1; + SPEC_BSTR($$) = 0; + ignoreTypedefType = 1; + } + | sfr_attributes + ; + +sfr_attributes + : SFR { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_CHAR; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + | SFR BANKED { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 1; + SPEC_NOUN($$) = V_CHAR; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +sfr_attributes + : SFR16 { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_INT; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +sfr_attributes + : SFR32 { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_INT; + SPEC_SCLS($$) = S_SFR; + SPEC_LONG($$) = 1; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +opt_stag + : stag + | { /* synthesize a name add to structtable */ + ignoreTypedefType = 0; + $$ = newStruct(genSymName(NestLevel)); + $$->level = NestLevel; + $$->block = currBlockno; + $$->tagsym = NULL; + //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); + } + ; + +stag + : identifier + { /* add name to structure table */ + ignoreTypedefType = 0; + $$ = newStruct($1->name); + $$->level = NestLevel; + $$->block = currBlockno; + $$->tagsym = $1; + //$$ = findSymWithBlock (StructTab, $1, currBlockno); + //if (! $$ ) + // { + // $$ = newStruct($1->name); + // $$->level = NestLevel; + // $$->tagsym = $1; + // //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); + // } + } + ; + +opt_assign_expr + : '=' constant_expr + { + value *val; + + val = constExprValue($2, TRUE); + if (!val) // Not a constant expression + { + werror (E_CONST_EXPECTED); + val = constIntVal("0"); + } + else if (!IS_INT(val->type) && !IS_CHAR(val->type) && !IS_BOOL(val->type)) + { + werror(E_ENUM_NON_INTEGER); + SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(val)); + val = constVal(lbuff); + } + $$ = cenum = val; + } + | { + if (cenum) + { + SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(cenum)+1); + $$ = cenum = constVal(lbuff); + } + else + { + $$ = cenum = constCharVal(0); + } + } + ; + +specifier_qualifier_list : type_specifier_list_ { $$ = finalizeSpec($1); }; + +type_specifier_list_ + : type_specifier_qualifier + //| type_specifier_list_ type_specifier { $$ = mergeSpec ($1,$2, "type_specifier_list"); } + | type_specifier_list_ type_specifier_qualifier { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "type_specifier_list type_specifier skipped"); + } + ; + +identifier_list + : identifier + | identifier_list ',' identifier + { + $3->next = $1; + $$ = $3; + } + ; + +type_name + : declaration_specifiers + { + if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) + { + werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); + } + $$ = $1; ignoreTypedefType = 0; + } + | declaration_specifiers abstract_declarator + { + /* go to the end of the list */ + sym_link *p; + + if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) + { + werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); + } + pointerTypes ($2,$1); + for (p = $2; p && p->next; p = p->next) + ; + if (!p) + { + werror(E_SYNTAX_ERROR, yytext); + } + else + { + p->next = $1; + } + $$ = $2; + ignoreTypedefType = 0; + } + ; + +critical + : CRITICAL { + if (inCriticalFunction || inCriticalBlock) + werror(E_INVALID_CRITICAL); + inCriticalBlock = 1; + STACK_PUSH(continueStack,NULL); + STACK_PUSH(breakStack,NULL); + $$ = NULL; + } + ; + +critical_statement + : critical statement { + STACK_POP(breakStack); + STACK_POP(continueStack); + $$ = newNode(CRITICAL,$2,NULL); + inCriticalBlock = 0; + } + ; + +statements_and_implicit + : statement_list + | statement_list implicit_block + { + $$ = newNode(NULLOP, $1, $2); + if (!options.std_c99) + werror(E_DECL_AFTER_STATEMENT_C99); + } + ; + +declaration_after_statement + : { + NestLevel += SUBLEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + ignoreTypedefType = 0; + } + declaration_list { $$ = $2; } + ; + +implicit_block + : declaration_after_statement statements_and_implicit + { + NestLevel -= SUBLEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + $$ = createBlock($1, $2); + cleanUpLevel(StructTab, NestLevel + SUBLEVEL_UNIT); + } + | declaration_after_statement + { + NestLevel -= SUBLEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + $$ = createBlock($1, NULL); + cleanUpLevel(StructTab, NestLevel + SUBLEVEL_UNIT); + } + ; + +declaration_list + : declaration + { + /* if this is typedef declare it immediately */ + if ( $1 && IS_TYPEDEF($1->etype)) { + allocVariables ($1); + $$ = NULL; + } + else + $$ = $1; + ignoreTypedefType = 0; + addSymChain(&$1); + } + + | declaration_list declaration + { + symbol *sym; + + /* if this is a typedef */ + if ($2 && IS_TYPEDEF($2->etype)) { + allocVariables ($2); + $$ = $1; + } + else { + /* get to the end of the previous decl */ + if ( $1 ) { + $$ = sym = $1; + while (sym->next) + sym = sym->next; + sym->next = $2; + } + else + $$ = $2; + } + ignoreTypedefType = 0; + addSymChain(&$2); + } + ; + +statement_list + : statement + | statement_list statement { $$ = newNode(NULLOP,$1,$2);} + ; + +else_statement + : ELSE statement { $$ = $2; } + | { $$ = NULL; } + ; + + + +while : WHILE { /* create and push the continue , break & body labels */ + static int Lblnum = 0; + /* continue */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilecontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilebreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* body */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilebody_%d",Lblnum++); + $$ = newSymbol(lbuff,NestLevel); + } + ; + +do : DO { /* create and push the continue , break & body Labels */ + static int Lblnum = 0; + + /* continue */ + SNPRINTF(lbuff, sizeof(lbuff), "_docontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF(lbuff, sizeof(lbuff), "_dobreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* do body */ + SNPRINTF(lbuff, sizeof(lbuff), "_dobody_%d",Lblnum++); + $$ = newSymbol (lbuff,NestLevel); + } + ; + +for : FOR { /* create & push continue, break & body labels */ + static int Lblnum = 0; + + NestLevel += LEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + ignoreTypedefType = 0; + + /* continue */ + SNPRINTF(lbuff, sizeof(lbuff), "_forcontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF(lbuff, sizeof(lbuff), "_forbreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* body */ + SNPRINTF(lbuff, sizeof(lbuff), "_forbody_%d",Lblnum); + $$ = newSymbol(lbuff,NestLevel); + /* condition */ + SNPRINTF(lbuff, sizeof(lbuff), "_forcond_%d",Lblnum++); + STACK_PUSH(forStack,newSymbol(lbuff,NestLevel)); + } + ; + asm_string_literal : STRING_LITERAL ; @@ -1968,7 +2588,7 @@ asm_statement seqPointNo++; ex = newNode(INLINEASM, NULL, NULL); - ex->values.inlineasm = copyStr ($3, NULL); + ex->values.inlineasm = strdup(copyStr ($3, NULL)); seqPointNo++; $$ = ex; } diff --git a/src/SDCCBBlock.c b/src/SDCCBBlock.c index 3e6c666da..baf865a60 100644 --- a/src/SDCCBBlock.c +++ b/src/SDCCBBlock.c @@ -29,21 +29,15 @@ int eBBNum = 0; set *graphEdges = NULL; /* list of edges in this flow graph */ -struct _dumpFiles dumpFiles[] = {{DUMP_RAW0, ".dumpraw0", NULL}, - {DUMP_RAW1, ".dumpraw1", NULL}, - {DUMP_CSE, ".dumpcse", NULL}, - {DUMP_DFLOW, ".dumpdflow", NULL}, - {DUMP_GCSE, ".dumpgcse", NULL}, - {DUMP_DEADCODE, ".dumpdeadcode", NULL}, - {DUMP_LOOP, ".dumploop", NULL}, - {DUMP_LOOPG, ".dumploopg", NULL}, - {DUMP_LOOPD, ".dumploopd", NULL}, - {DUMP_RANGE, ".dumprange", NULL}, - {DUMP_PACK, ".dumppack", NULL}, - {DUMP_RASSGN, ".dumprassgn", NULL}, - {DUMP_LRANGE, ".dumplrange", NULL}, - {DUMP_LOSPRE, ".dumplospre", NULL}, - {0, NULL, NULL}}; +struct _dumpFiles dumpFiles[] = { + {DUMP_RAW0, ".dumpraw0", NULL}, {DUMP_RAW1, ".dumpraw1", NULL}, + {DUMP_CSE, ".dumpcse", NULL}, {DUMP_DFLOW, ".dumpdflow", NULL}, + {DUMP_GCSE, ".dumpgcse", NULL}, {DUMP_DEADCODE, ".dumpdeadcode", NULL}, + {DUMP_LOOP, ".dumploop", NULL}, {DUMP_LOOPG, ".dumploopg", NULL}, + {DUMP_LOOPD, ".dumploopd", NULL}, {DUMP_RANGE, ".dumprange", NULL}, + {DUMP_PACK, ".dumppack", NULL}, {DUMP_RASSGN, ".dumprassgn", NULL}, + {DUMP_LRANGE, ".dumplrange", NULL}, {DUMP_LOSPRE, ".dumplospre", NULL}, + {DUMP_CUSTOM, ".dumpcustom", NULL}, {0, NULL, NULL}}; /*-----------------------------------------------------------------*/ /* printEntryLabel - prints entry label of a ebblock */ @@ -111,7 +105,8 @@ FILE *createDumpFile(int id) { #endif dbuf_append_str(&dumpFileName, dumpFilesPtr->ext); if (!(dumpFilesPtr->filePtr = fopen(dbuf_c_str(&dumpFileName), "w"))) { - werror(E_FILE_OPEN_ERR, dbuf_c_str(&dumpFileName)); + werror(E_OUTPUT_FILE_OPEN_ERR, dbuf_c_str(&dumpFileName), + strerror(errno)); dbuf_destroy(&dumpFileName); exit(1); } @@ -195,9 +190,11 @@ void dumpEbbsToFileExt(int id, ebbIndex *ebbi) { fprintf( of, "\n----------------------------------------------------------------\n"); - fprintf(of, "Basic Block %s (df:%d bb:%d lvl:%d): loopDepth=%d%s%s%s\n", + fprintf(of, + "Basic Block %s (df:%d bb:%d lvl:%ld:%ld): loopDepth=%d%s%s%s\n", ebbs[i]->entryLabel->name, ebbs[i]->dfnum, ebbs[i]->bbnum, - ebbs[i]->entryLabel->level, ebbs[i]->depth, + ebbs[i]->entryLabel->level / LEVEL_UNIT, + ebbs[i]->entryLabel->level % LEVEL_UNIT, ebbs[i]->depth, ebbs[i]->noPath ? " noPath" : "", ebbs[i]->partOfLoop ? " partOfLoop" : "", ebbs[i]->isLastInLoop ? " isLastInLoop" : ""); @@ -356,7 +353,7 @@ eBBlock *iCode2eBBlock(iCode *ic) { /*-----------------------------------------------------------------*/ /* eBBWithEntryLabel - finds the basic block with the entry label */ /*-----------------------------------------------------------------*/ -eBBlock *eBBWithEntryLabel(ebbIndex *ebbi, symbol *eLabel) { +eBBlock *eBBWithEntryLabel(ebbIndex *ebbi, const symbol *eLabel) { eBBlock **ebbs = ebbi->bbOrder; int count = ebbi->count; int i; @@ -682,6 +679,9 @@ void replaceLabel(eBBlock *ebp, symbol *fromLbl, symbol *toLbl) { else if (isSymbolEqual(IC_FALSE(ic), fromLbl)) IC_FALSE(ic) = toLbl; break; + + case JUMPTABLE: + replaceSetItem(IC_JTLABELS(ic), fromLbl, toLbl); } } @@ -700,8 +700,9 @@ iCode *iCodeFromeBBlock(eBBlock **ebbs, int count) { if (ebbs[i]->sch == NULL) continue; - if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && - ebbs[i]->entryLabel != returnLabel)) { + if (ebbs[i]->noPath && optimize.label4 && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) { iCode *ic = NULL; bool foundNonlabel = 0; ic = ebbs[i]->sch; @@ -768,3 +769,22 @@ int otherPathsPresent(eBBlock **ebbs, eBBlock *this) { else return 1; } + +/*-----------------------------------------------------------------*/ +/* freeBBlockData - Deallocate data structures associated with */ +/* the current blocks. They will all be recomputed if the */ +/* iCode chain is divided into blocks again later. */ +/*-----------------------------------------------------------------*/ +void freeeBBlockData(ebbIndex *ebbi) { + int i; + eBBlock **ebbs = ebbi->bbOrder; + + for (i = 0; i < ebbi->count; i++) { + deleteSet(&ebbs[i]->succList); + deleteSet(&ebbs[i]->predList); + freeBitVect(ebbs[i]->succVect); + freeBitVect(ebbs[i]->domVect); + + freeCSEdata(ebbs[i]); + } +} diff --git a/src/SDCCBBlock.h b/src/SDCCBBlock.h index dcda79c56..2ed57800c 100644 --- a/src/SDCCBBlock.h +++ b/src/SDCCBBlock.h @@ -92,7 +92,7 @@ extern set *graphEdges; DEFSETFUNC(printEntryLabel); eBBlock *neweBBlock(); edge *newEdge(eBBlock *, eBBlock *); -eBBlock *eBBWithEntryLabel(ebbIndex *, symbol *); +eBBlock *eBBWithEntryLabel(ebbIndex *, const symbol *); DEFSETFUNC(ifFromIs); set *edgesTo(eBBlock *); void remiCodeFromeBBlock(eBBlock *, iCode *); @@ -105,5 +105,6 @@ void replaceLabel(eBBlock *, symbol *, symbol *); void dumpEbbsToFileExt(int, ebbIndex *); void dumpLiveRanges(int, hTab *liveRanges); void closeDumpFiles(); +void freeeBBlockData(ebbIndex *); #endif diff --git a/src/SDCCargs.h b/src/SDCCargs.h index bd2e9c091..a77488572 100644 --- a/src/SDCCargs.h +++ b/src/SDCCargs.h @@ -61,6 +61,6 @@ typedef struct { } OPTION; char *getStringArg(const char *szStart, char **argv, int *pi, int argc); -int getIntArg(const char *szStart, char **argv, int *pi, int argc); +long getIntArg(const char *szStart, char **argv, int *pi, int argc); #endif diff --git a/src/SDCCasm.c b/src/SDCCasm.c index 259e3b009..322fec731 100644 --- a/src/SDCCasm.c +++ b/src/SDCCasm.c @@ -31,6 +31,7 @@ assemblers. #include +#include "SDCCsymt.h" #include "common.h" #include "dbuf_string.h" @@ -53,7 +54,7 @@ const char *FileBaseName(const char *fileFullName) { void dbuf_tvprintf(struct dbuf_s *dbuf, const char *format, va_list ap) { /* Under Linux PPC va_list is a structure instead of a primitive type, - and doesnt like being passed around. This version turns everything + and doesn't like being passed around. This version turns everything into one function. Supports: @@ -224,6 +225,8 @@ const char *printILine(iCode *ic) { struct dbuf_s tmpBuf; iCodeTable *icTab = getTableEntry(ic->op); + wassert(icTab); + dbuf_init(&tmpBuf, 1024); if (INLINEASM == ic->op) diff --git a/src/SDCCast.c b/src/SDCCast.c index 218edc37c..a7c75b7de 100644 --- a/src/SDCCast.c +++ b/src/SDCCast.c @@ -24,7 +24,10 @@ #define DEBUG_CF(x) /* puts(x); */ +#include + #include "SDCCbtree.h" +#include "SDCCset.h" #include "common.h" #include "dbuf_string.h" @@ -65,7 +68,7 @@ static struct { int noLineno = 0; int noAlloc = 0; symbol *currFunc = NULL; -static ast *createIval(ast *, sym_link *, initList *, ast *, ast *); +static ast *createIval(ast *, sym_link *, initList *, ast *, ast *, int); static ast *createIvalCharPtr(ast *, sym_link *, ast *, ast *); static ast *optimizeCompare(ast *); ast *optimizeRRCRLC(ast *); @@ -75,10 +78,13 @@ ast *optimizeGetAbit(ast *, RESULT_TYPE); ast *optimizeGetByte(ast *, RESULT_TYPE); ast *optimizeGetWord(ast *, RESULT_TYPE); static ast *backPatchLabels(ast *, symbol *, symbol *); +static void copyAstLoc(ast *, ast *); void PA(ast *t); int inInitMode = 0; memmap *GcurMemmap = NULL; /* points to the memmap that's currently active */ struct dbuf_s *codeOutBuf; +extern int inCriticalFunction; +extern int inCriticalBlock; int ptt(ast *tree) { printTypeChain(tree->ftype, stdout); @@ -202,13 +208,24 @@ void copyAstValues(ast *dest, ast *src) { /* copyAst - makes a copy of a given astession */ /*-----------------------------------------------------------------*/ ast *copyAst(ast *src) { - ast *dest; + ast *dest = NULL; + static long level = -1; + static set *pset; if (!src) return NULL; - dest = Safe_alloc(sizeof(ast)); + if (++level == 0) + if ((pset = newSet()) == NULL) + goto exit_1; + + /* check if it is a infinate recursive call */ + if (isinSet(pset, src)) + goto exit_1; + else + addSet(&pset, src); + dest = Safe_alloc(sizeof(ast)); dest->type = src->type; dest->filename = src->filename; dest->lineno = src->lineno; @@ -241,7 +258,14 @@ ast *copyAst(ast *src) { dest->falseLabel = copySymbol(src->falseLabel); dest->left = copyAst(src->left); dest->right = copyAst(src->right); + exit: + if (pset != NULL && isinSet(pset, src)) + deleteSetItem(&pset, src); +exit_1: + if (level-- == 0) + if (pset != NULL) + deleteSet(&pset); return dest; } @@ -419,6 +443,9 @@ bool hasSEFcalls(ast *tree) { tree->opval.op == DEC_OP)) return TRUE; + if (astHasVolatile(tree)) + return TRUE; + return (hasSEFcalls(tree->left) | hasSEFcalls(tree->right)); } @@ -570,7 +597,7 @@ ast *resolveSymbols(ast *tree) { /* if not found in the symbol table */ /* mark it as undefined & assume it */ /* is an integer in data space */ - if (!csym && !tree->opval.val->sym->implicit) { + if (!csym && !tree->opval.val->sym->implicit && !tree->opval.val->type) { /* if this is a function name then */ /* mark it as returning an int */ if (tree->funcName) { @@ -712,13 +739,14 @@ symbol *funcOfTypeVarg(const char *name, const char *rtype, int nArgs, /*-----------------------------------------------------------------*/ /* reverseParms - will reverse a parameter tree */ /*-----------------------------------------------------------------*/ -static void reverseParms(ast *ptree) { +static void reverseParms(ast *ptree, int r) { ast *ttree; if (!ptree) return; /* top down if we find a nonParm tree then quit */ - if (ptree->type == EX_OP && ptree->opval.op == PARAM && !ptree->reversed) { + if (ptree->type == EX_OP && ptree->opval.op == PARAM && + ptree->reversed != r) { /* The various functions expect the parameter tree to be right heavy. */ /* Rotate the tree to be left heavy so that after reversal it is */ /* right heavy again. */ @@ -734,9 +762,9 @@ static void reverseParms(ast *ptree) { ttree = ptree->left; ptree->left = ptree->right; ptree->right = ttree; - ptree->reversed = 1; - reverseParms(ptree->left); - reverseParms(ptree->right); + ptree->reversed = r; + reverseParms(ptree->left, r); + reverseParms(ptree->right, r); } return; @@ -878,23 +906,15 @@ static int processParms(ast *func, value *defParm, ast **actParm, /* if defined parameters ended but actual has not & */ /* reentrant */ - if (!defParm && *actParm && (options.stackAuto || IFFUNC_ISREENT(functype))) + if (!defParm && *actParm && options.stackAuto) { return 0; - + } resolveSymbols(*actParm); /* the parameter type must be at least castable */ if (compareType(defParm->type, (*actParm)->ftype) == 0) { - if (IS_STRUCT((*actParm)->ftype)) { - if (IS_AST_VALUE(*actParm)) - werrorfl((*actParm)->filename, (*actParm)->lineno, E_STRUCT_AS_ARG, - (*actParm)->opval.val->name); - else - werrorfl((*actParm)->filename, (*actParm)->lineno, E_STRUCT_AS_ARG, ""); - } else { - werror(E_INCOMPAT_TYPES); - printFromToType((*actParm)->ftype, defParm->type); - } + werror(E_INCOMPAT_TYPES); + printFromToType((*actParm)->ftype, defParm->type); return 1; } @@ -925,7 +945,7 @@ static int processParms(ast *func, value *defParm, ast **actParm, /* this parameter is not passed in registers */ /* then the function must be defined reentrant */ if (IS_FUNCPTR(func->ftype) && !SPEC_REGPARM((*actParm)->etype) && - !IFFUNC_ISREENT(functype) && !options.stackAuto) { + !options.stackAuto) { werror(E_NONRENT_ARGS); fatalError++; return 1; @@ -993,6 +1013,8 @@ static symbol *findStructField(symbol *fields, symbol *target) { return NULL; /* not found */ } +static int aggregateIsAutoVar = 0; + /*-----------------------------------------------------------------*/ /* createIvalStruct - generates initial value for structures */ /*-----------------------------------------------------------------*/ @@ -1000,7 +1022,7 @@ static ast *createIvalStruct(ast *sym, sym_link *type, initList *ilist, ast *rootValue) { ast *rast = NULL; ast *lAst; - symbol *sflds; + symbol *sflds, *old_sflds, *ps; initList *iloop; sym_link *etype = getSpec(type); @@ -1016,6 +1038,7 @@ static ast *createIvalStruct(ast *sym, sym_link *type, initList *ilist, if (sflds && IS_BITFIELD(sflds->type) && SPEC_BUNNAMED(sflds->etype)) continue; + old_sflds = sflds; /* designated initializer? */ if (iloop && iloop->designation) { if (iloop->designation->type != DESIGNATOR_STRUCT) { @@ -1042,15 +1065,30 @@ static ast *createIvalStruct(ast *sym, sym_link *type, initList *ilist, if (!iloop && (!AST_SYMBOL(rootValue)->islocal || SPEC_STAT(etype))) break; + if (aggregateIsAutoVar && (SPEC_STRUCT(type)->type != UNION)) + for (ps = old_sflds; ps != sflds && ps != NULL; ps = ps->next) { + ps->implicit = 1; + lAst = newNode(PTR_OP, newNode('&', sym, NULL), + newAst_VALUE(symbolVal(ps))); + lAst = decorateType(resolveSymbols(lAst), RESULT_TYPE_NONE); + rast = decorateType(resolveSymbols(createIval(lAst, ps->type, NULL, + rast, rootValue, 1)), + RESULT_TYPE_NONE); + } + /* initialize this field */ sflds->implicit = 1; lAst = newNode(PTR_OP, newNode('&', sym, NULL), newAst_VALUE(symbolVal(sflds))); lAst = decorateType(resolveSymbols(lAst), RESULT_TYPE_NONE); - rast = decorateType( - resolveSymbols(createIval(lAst, sflds->type, iloop, rast, rootValue)), - RESULT_TYPE_NONE); + rast = decorateType(resolveSymbols(createIval(lAst, sflds->type, iloop, + rast, rootValue, 1)), + RESULT_TYPE_NONE); iloop = iloop ? iloop->next : NULL; + + /* Unions can only initialize a single field */ + if (SPEC_STRUCT(type)->type == UNION) + break; } if (iloop) { @@ -1078,12 +1116,23 @@ static ast *createIvalArray(ast *sym, sym_link *type, initList *ilist, /* take care of the special case */ /* array of characters can be init */ /* by a string */ - if (IS_CHAR(type->next) && ilist && ilist->type == INIT_NODE) + /* char *p = "abc"; */ + if ((IS_CHAR(type->next) || IS_INT(type->next) && IS_UNSIGNED(type->next)) && + ilist && ilist->type == INIT_NODE) if ((rast = createIvalCharPtr( sym, type, decorateType(resolveSymbols(list2expr(ilist)), RESULT_TYPE_NONE), rootValue))) - + return decorateType(resolveSymbols(rast), RESULT_TYPE_NONE); + /* char *p = {"abc"}; */ + if ((IS_CHAR(type->next) || IS_INT(type->next) && IS_UNSIGNED(type->next)) && + ilist && ilist->type == INIT_DEEP && ilist->init.deep && + ilist->init.deep->type == INIT_NODE) + if ((rast = createIvalCharPtr( + sym, type, + decorateType(resolveSymbols(list2expr(ilist->init.deep)), + RESULT_TYPE_NONE), + rootValue))) return decorateType(resolveSymbols(rast), RESULT_TYPE_NONE); /* not the special case */ @@ -1120,9 +1169,8 @@ static ast *createIvalArray(ast *sym, sym_link *type, initList *ilist, } else { for (;;) { ast *aSym; - if (!iloop && ((lcnt && size >= lcnt) || !DCL_ELEM(type) || - !AST_SYMBOL(rootValue)->islocal || SPEC_STAT(etype))) { + !AST_SYMBOL(rootValue)->islocal)) { break; } @@ -1152,7 +1200,7 @@ static ast *createIvalArray(ast *sym, sym_link *type, initList *ilist, aSym = newNode('[', sym, newAst_VALUE(valueFromLit((float)(idx)))); aSym = decorateType(resolveSymbols(aSym), RESULT_TYPE_NONE); - rast = createIval(aSym, type->next, iloop, rast, rootValue); + rast = createIval(aSym, type->next, iloop, rast, rootValue, 0); idx++; iloop = (iloop ? iloop->next : NULL); } @@ -1181,7 +1229,8 @@ static ast *createIvalCharPtr(ast *sym, sym_link *type, ast *iexpr, /* if this is a pointer & right is a literal array then */ /* just assignment will do */ if (IS_PTR(type) && - ((IS_LITERAL(iexpr->etype) || SPEC_SCLS(iexpr->etype) == S_CODE) && + ((IS_LITERAL(iexpr->etype) || + (IS_SPEC(iexpr->etype) && SPEC_SCLS(iexpr->etype) == S_CODE)) && IS_ARRAY(iexpr->ftype))) return newNode('=', sym, iexpr); @@ -1190,7 +1239,7 @@ static ast *createIvalCharPtr(ast *sym, sym_link *type, ast *iexpr, /* for each character generate an assignment */ /* to the array element */ unsigned int i = 0; - unsigned int symsize = getSize(type); + unsigned int symsize = DCL_ELEM(type); if (!AST_SYMBOL(rootVal)->islocal || SPEC_STAT(getSpec(type))) return NULL; @@ -1205,20 +1254,27 @@ static ast *createIvalCharPtr(ast *sym, sym_link *type, ast *iexpr, return decorateType(resolveSymbols(rast), RESULT_TYPE_NONE); } - if ((IS_LITERAL(iexpr->etype) || SPEC_SCLS(iexpr->etype) == S_CODE) && + if ((IS_LITERAL(iexpr->etype) || + (IS_SPEC(iexpr->etype) && SPEC_SCLS(iexpr->etype) == S_CODE)) && IS_ARRAY(iexpr->ftype)) { /* for each character generate an assignment */ /* to the array element */ - const char *s = SPEC_CVAL(iexpr->etype).v_char; unsigned int i = 0; - unsigned int symsize = getSize(type); + unsigned int symsize = DCL_ELEM(type); - size = getSize(iexpr->ftype); + size = DCL_ELEM(iexpr->ftype); if (symsize && size > symsize) { if (size > symsize) { char *name = (IS_AST_SYM_VALUE(sym)) ? AST_SYMBOL(sym)->name : ""; - if (options.std_c99 && s[symsize] == '\0' && size == symsize + 1) { + TYPE_TARGET_ULONG c; + if (IS_CHAR(type->next)) + c = SPEC_CVAL(iexpr->etype).v_char[symsize]; + else if (!IS_LONG(type->next)) + c = SPEC_CVAL(iexpr->etype).v_char16[symsize]; + else + c = SPEC_CVAL(iexpr->etype).v_char32[symsize]; + if (options.std_c99 && c == '\0' && size == symsize + 1) { if (!options.lessPedantic) werrorfl(iexpr->filename, iexpr->lineno, W_STRING_CANNOT_BE_TERMINATED, name); @@ -1230,10 +1286,25 @@ static ast *createIvalCharPtr(ast *sym, sym_link *type, ast *iexpr, } for (i = 0; i < size; i++) { + TYPE_TARGET_ULONG c; + if (IS_CHAR(type->next)) + c = SPEC_CVAL(iexpr->etype).v_char[i]; + else if (!IS_LONG(type->next)) + c = SPEC_CVAL(iexpr->etype).v_char16[i]; + else + c = SPEC_CVAL(iexpr->etype).v_char32[i]; + rast = newNode( + NULLOP, rast, + newNode('=', newNode('[', sym, newAst_VALUE(valueFromLit((float)i))), + newAst_VALUE(valueFromLit(c)))); + } + + /* assign zero to others */ + for (i = size; i < symsize; i++) { rast = newNode( NULLOP, rast, newNode('=', newNode('[', sym, newAst_VALUE(valueFromLit((float)i))), - newAst_VALUE(valueFromLit(*s++)))); + newAst_VALUE(valueFromLit(0)))); } // now WE don't need iexpr's symbol anymore @@ -1272,7 +1343,7 @@ static ast *createIvalPtr(ast *sym, sym_link *type, initList *ilist, iexpr = newAst_VALUE(valueFromLit(0)); /* if character pointer */ - if (IS_CHAR(type->next)) + if (IS_CHAR(type->next) || IS_INT(type->next) && IS_UNSIGNED(type->next)) if ((rast = createIvalCharPtr(sym, type, iexpr, rootVal))) return rast; @@ -1283,10 +1354,11 @@ static ast *createIvalPtr(ast *sym, sym_link *type, initList *ilist, /* createIval - generates code for initial value */ /*-----------------------------------------------------------------*/ static ast *createIval(ast *sym, sym_link *type, initList *ilist, ast *wid, - ast *rootValue) { + ast *rootValue, int omitStatic) { ast *rast = NULL; - if (!ilist && (!AST_SYMBOL(rootValue)->islocal || SPEC_STAT(getSpec(type)))) + if (!ilist && (!AST_SYMBOL(rootValue)->islocal || + (omitStatic && SPEC_STAT(getSpec(type))))) return NULL; /* if structure then */ @@ -1317,7 +1389,7 @@ static ast *createIval(ast *sym, sym_link *type, initList *ilist, ast *wid, /*-----------------------------------------------------------------*/ ast *initAggregates(symbol *sym, initList *ival, ast *wid) { ast *newAst = newAst_VALUE(symbolVal(sym)); - return createIval(newAst, sym->type, ival, wid, newAst); + return createIval(newAst, sym->type, ival, wid, newAst, 1); } /*-----------------------------------------------------------------*/ @@ -1384,7 +1456,9 @@ static ast *gatherAutoInit(symbol *autoChain) { setAstFileLine(ilist->init.node, sym->fileDef, sym->lineDef); if (IS_AGGREGATE(sym->type)) { + aggregateIsAutoVar = 1; work = initAggregates(sym, sym->ival, NULL); + aggregateIsAutoVar = 0; } else { if (getNelements(sym->type, sym->ival) > 1) { werrorfl(sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", @@ -1424,7 +1498,7 @@ void freeStringSymbol(symbol *sym) { /*-----------------------------------------------------------------*/ /* stringToSymbol - creates a symbol from a literal string */ /*-----------------------------------------------------------------*/ -static value *stringToSymbol(value *val) { +value *stringToSymbol(value *val) { struct dbuf_s dbuf; static int charLbl = 0; symbol *sym; @@ -1466,6 +1540,10 @@ static value *stringToSymbol(value *val) { /* allocate it */ addSymChain(&sym); allocVariables(sym); + } else { + defaultOClass(sym); + addSet(&strSym, sym); + addSet(&statsg->syms, sym); } sym->ival = NULL; return symbolVal(sym); @@ -1503,6 +1581,11 @@ ast *processBlockVars(ast *tree, int *stack, int action) { } } + if (IS_FOR_STMT(tree)) { + processBlockVars(AST_FOR(tree, initExpr), stack, action); + processBlockVars(AST_FOR(tree, condExpr), stack, action); + processBlockVars(AST_FOR(tree, loopExpr), stack, action); + } processBlockVars(tree->left, stack, action); processBlockVars(tree->right, stack, action); @@ -1611,8 +1694,9 @@ value *constExprValue(ast *cexpr, int check) { /* if we are casting a literal value then */ if (IS_CAST_OP(cexpr) && IS_LITERAL(cexpr->right->ftype)) { - return valCastLiteral(cexpr->ftype, - floatFromVal(cexpr->right->opval.val)); + return valCastLiteral( + cexpr->ftype, floatFromVal(cexpr->right->opval.val), + (TYPE_TARGET_ULONGLONG)ullFromVal(cexpr->right->opval.val)); } if (IS_AST_VALUE(cexpr)) { @@ -1697,8 +1781,7 @@ static bool isLoopCountable(ast *initExpr, ast *condExpr, ast *loopExpr, IS_AST_SYM_VALUE(condExpr->left->left) && isSymbolEqual(*sym, AST_SYMBOL(condExpr->left->left))) { - *end = - newNode('+', condExpr->left->right, newAst_VALUE(constCharVal(1))); + *end = newNode('+', condExpr->left->right, newAst_VALUE(constVal("1"))); break; } return FALSE; @@ -1938,7 +2021,7 @@ bool isConformingBody(ast *pbody, symbol *sym, ast *body) { case NE_OP: case '?': case ':': - case SIZEOF: /* evaluate wihout code generation */ + case SIZEOF: /* evaluate without code generation */ if (IS_AST_SYM_VALUE(pbody->left) && isSymbolEqual(AST_SYMBOL(pbody->left), sym)) @@ -2076,7 +2159,7 @@ static void replLoopSym(ast *body, symbol *sym) { body->type = EX_OP; body->opval.op = '-'; body->left = newAst_VALUE(symbolVal(sym)); - body->right = newAst_VALUE(constCharVal(1)); + body->right = newAst_VALUE(constVal("1")); } return; } @@ -2120,7 +2203,7 @@ ast *reverseLoop(ast *loop, symbol *sym, ast *init, ast *end) { newNode(NULLOP, loop->left, newNode(NULLOP, newNode(SUB_ASSIGN, newAst_VALUE(symbolVal(sym)), - newAst_VALUE(constCharVal(1))), + newAst_VALUE(constVal("1"))), rloop)))); rloop->lineno = init->lineno; @@ -2201,8 +2284,17 @@ static bool isInitiallyTrue(ast *initExpr, ast *condExpr) { /* Replace the symbol with its initial value and see if the condition */ /* simplifies to a non-zero (TRUE) literal value */ condExpr = copyAst(condExpr); - if (replLoopSymByVal(condExpr, sym, AST_VALUE(initExpr))) + if (replLoopSymByVal(condExpr, sym, AST_VALUE(initExpr))) { + int original; + + /* We are speculating that the condition is always true */ + /* or false for the first loop iteration; no need to */ + /* trigger a warning that may not apply to the original */ + /* source code. */ + original = setWarningDisabledState(W_COMP_RANGE, TRUE); condExpr = decorateType(condExpr, RESULT_TYPE_NONE); + setWarningDisabledState(W_COMP_RANGE, original); + } if (!IS_AST_LIT_VALUE(condExpr)) return FALSE; return !isEqualVal(AST_VALUE(condExpr), 0); @@ -2326,15 +2418,20 @@ getResultTypeFromType(sym_link *type) { if (IS_BITFIELD(type)) { unsigned blen = SPEC_BLEN(type); - if (blen <= 1) - return RESULT_TYPE_BOOL; + /* BOOL and single bit BITFIELD are not interchangeable! + * There must be a cast to do this safely, in which case + * the previous IS_BOOLEAN test will handle it. + + if (blen <= 1) + return RESULT_TYPE_BOOL; + */ if (blen <= 8) return RESULT_TYPE_CHAR; return RESULT_TYPE_INT; } if (IS_CHAR(type)) return RESULT_TYPE_CHAR; - if (IS_INT(type) && !IS_LONG(type)) + if (IS_INT(type) && !IS_LONG(type) && !IS_LONGLONG(type)) return RESULT_TYPE_INT; return RESULT_TYPE_OTHER; } @@ -2373,6 +2470,7 @@ static ast *addCast(ast *tree, RESULT_TYPE resultType, bool promote) { newLink = newCharLink(); break; case RESULT_TYPE_INT: + case RESULT_TYPE_GPTR: #if 0 if (getSize (tree->etype) > INTSIZE) { @@ -2413,10 +2511,17 @@ static ast *addCast(ast *tree, RESULT_TYPE resultType, bool promote) { /* resultTypePropagate - decides if resultType can be propagated */ /*-----------------------------------------------------------------*/ static RESULT_TYPE resultTypePropagate(ast *tree, RESULT_TYPE resultType) { + /* In general, we don't want to propagate BOOL or IFX result types */ + if (((resultType == RESULT_TYPE_BOOL) || (resultType == RESULT_TYPE_IFX)) && + (tree->opval.op != '=')) + resultType = RESULT_TYPE_NONE; + switch (tree->opval.op) { case AND_OP: case OR_OP: case '!': + /* Logical operators should always propagate to boolean */ + return RESULT_TYPE_BOOL; case '=': case '?': case ':': @@ -2473,8 +2578,10 @@ static RESULT_TYPE getLeftResultType(ast *tree, RESULT_TYPE resultType) { case '[': if (!IS_ARRAY(LTYPE(tree))) return resultType; - if (DCL_ELEM(LTYPE(tree)) > 0 && DCL_ELEM(LTYPE(tree)) <= 255) - return RESULT_TYPE_CHAR; + if (DCL_ELEM(LTYPE(tree)) > 0 && getSize(tree->left->ftype) < 128) + return RESULT_TYPE_CHAR; // TODO: Instead of doing this optimization here, + // do it later on the iCode (where it probably + // could use a more efficient unsigned char). return resultType; default: return resultType; @@ -2570,7 +2677,8 @@ void CodePtrPointsToConst(sym_link *t) { /*-----------------------------------------------------------------*/ /* checkPtrCast - if casting to/from pointers, do some checking */ /*-----------------------------------------------------------------*/ -void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit) { +void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit, + bool orgIsNullPtrConstant) { int errors = 0; if (IS_ARRAY(orgType)) { @@ -2588,7 +2696,7 @@ void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit) { if (IS_INTEGRAL(orgType)) { // maybe this is NULL, then it's ok. if (!(IS_LITERAL(orgType) && (SPEC_CVAL(orgType).v_ulong == 0))) { - if (GPTRSIZE > FPTRSIZE && IS_GENPTR(newType) && + if (GPTRSIZE > FARPTRSIZE && IS_GENPTR(newType) && !IS_FUNCPTR(newType)) { // no way to set the storage if (IS_LITERAL(orgType)) { @@ -2614,10 +2722,22 @@ void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit) { getAddrspace(newType->next) != getAddrspace(orgType->next)) { errors += werror(E_INCOMPAT_PTYPES); } else if (IS_GENPTR(newType) && - IS_VOID(newType->next)) { // cast to void* is always allowed + IS_VOID(newType->next)) // cast to void* is always allowed + { + if (IS_FUNCPTR(orgType)) + errors += werror(FUNCPTRSIZE > GPTRSIZE ? E_INCOMPAT_PTYPES + : W_INCOMPAT_PTYPES); } else if (IS_GENPTR(orgType) && - IS_VOID(orgType->next)) { // cast from void* is always allowed - } else if (GPTRSIZE > FPTRSIZE) { + IS_VOID(orgType->next)) // cast from void* is always allowed - + // as long as we cast to a pointer to + // an object type + { + if (IS_FUNCPTR(newType) && + !orgIsNullPtrConstant) // cast to pointer to function is only + // allowed for null pointer constants + errors += werror(W_INCOMPAT_PTYPES); + } else if (GPTRSIZE > + FARPTRSIZE /*!TARGET_IS_Z80 && !TARGET_IS_GBZ80 */) { // if not a pointer to a function if (!(IS_CODEPTR(newType) && IS_FUNC(newType->next) && IS_FUNC(orgType))) { @@ -2653,6 +2773,344 @@ void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit) { } } +/*-----------------------------*/ +/* check if div or mod by zero */ +/*-----------------------------*/ +static void checkZero(value *val) { + if (!val) + return; + + if (IS_FLOAT(val->type) || IS_FIXED16X16(val->type)) { + if (floatFromVal(val) == 0.0) + werror(E_DIVIDE_BY_ZERO); + } else if (SPEC_LONGLONG(val->type)) { + if (ullFromVal(val) == 0LL) + werror(E_DIVIDE_BY_ZERO); + } else if (ulFromVal(val) == 0L) { + werror(E_DIVIDE_BY_ZERO); + } +} + +/*--------------------------------------------------------*/ +/* return true if subtree is somewhere in */ +/*--------------------------------------------------------*/ +static bool isAstInAst(ast *tree, ast *search) { + if (tree == search) + return TRUE; + if (!tree) + return FALSE; + if (tree->type == EX_OP) { + if (isAstInAst(tree->left, search)) + return TRUE; + if (isAstInAst(tree->right, search)) + return TRUE; + } + return FALSE; +} + +/*---------------------------------------*/ +/* make a simple copy of single ast node */ +/*---------------------------------------*/ +static ast *copyAstNode(ast *tree) { + ast *newtree; + + newtree = newAst_(tree->type); + newtree->lineno = tree->lineno; + newtree->filename = tree->filename; + newtree->level = tree->level; + newtree->block = tree->block; + newtree->initMode = tree->initMode; + newtree->seqPoint = tree->seqPoint; + newtree->funcName = tree->funcName; + newtree->reversed = tree->reversed; + newtree->decorated = tree->decorated; + + if (tree->ftype) + newtree->etype = getSpec(newtree->ftype = copyLinkChain(tree->ftype)); + + if (tree->type == EX_VALUE) { + newtree->opval.val = tree->opval.val; + } else if (tree->type == EX_LINK) { + newtree->opval.lnk = tree->opval.lnk; + } else { + newtree->opval.op = tree->opval.op; + copyAstValues(newtree, tree); + newtree->left = tree->left; + newtree->right = tree->right; + + newtree->trueLabel = tree->trueLabel; + newtree->falseLabel = tree->falseLabel; + } + + return newtree; +} + +/*-------------------------------------------------*/ +/* extract the ast subtrees that have side-effects */ +/*-------------------------------------------------*/ +static void rewriteAstGatherSideEffects(ast *tree, set **sideEffects) { + if (!tree) + return; + + if (tree->type == EX_OP) { + switch (tree->opval.op) { + case '=': + case INC_OP: + case DEC_OP: + case CALL: + case PCALL: + addSetHead(sideEffects, tree); + break; + default: + if (tree->left) + rewriteAstGatherSideEffects(tree->left, sideEffects); + if (tree->right) + rewriteAstGatherSideEffects(tree->right, sideEffects); + break; + } + return; + } + if (TETYPE(tree) && IS_VOLATILE(TETYPE(tree))) { + addSetHead(sideEffects, tree); + return; + } +} + +/*-------------------------------------------------------------------*/ +/* after rewriting a node, rejoin the portions of any old left/right */ +/* subtree that had side effects with a comma operator */ +/*-------------------------------------------------------------------*/ +static void rewriteAstJoinSideEffects(ast *tree, ast *oLeft, ast *oRight) { + set *sideEffects = NULL; + ast *sefTree; + + /* If the old left or right subtree has been orphaned, */ + /* gather any side-effects in it */ + if (oLeft && !isAstInAst(tree, oLeft)) + rewriteAstGatherSideEffects(oLeft, &sideEffects); + if (oRight && !isAstInAst(tree, oRight)) + rewriteAstGatherSideEffects(oRight, &sideEffects); + + /* Join any side-effects found */ + for (sefTree = setFirstItem(sideEffects); sefTree; + sefTree = setNextItem(sideEffects)) { + tree->right = copyAstNode(tree); + tree->left = sefTree; + tree->type = EX_OP; + tree->opval.op = ','; + } + + deleteSet(&sideEffects); +} + +static void optStdLibCall(ast *tree, RESULT_TYPE resulttype) { + ast *parms = tree->right; + ast *func = tree->left; + + if (!IS_FUNC(func->ftype) || IS_LITERAL(func->ftype) || + func->type != EX_VALUE || !func->opval.val->sym) + return; + + const char *funcname = func->opval.val->sym->name; + + unsigned int nparms = 0; + ast *parm; + for (parm = parms; parm && parm->type == EX_OP && parm->opval.op == PARAM; + parm = parm->right) + if (parm->left) + nparms++; + if (parm) + nparms++; + + // Optimize printf() to puts(). + if (!strcmp(funcname, "printf") && nparms == 1 && + resulttype == RESULT_TYPE_NONE) { + ast *parm = parms; + + if (parm->type == EX_OP && parm->opval.op == CAST) + parm = parm->right; + + if (parm->type != EX_VALUE || !IS_ARRAY(parm->opval.val->type) || + !parm->opval.val->sym) + return; + + size_t strlength = DCL_ELEM(parm->opval.val->type); + symbol *strsym = parm->opval.val->sym; + sym_link *strlink = strsym->etype; + + if (strsym->isstrlit != 1 || !strlink || !IS_SPEC(strlink) || + SPEC_NOUN(strlink) != V_CHAR) + return; + + for (size_t i = 0; i < strlength; i++) + if (SPEC_CVAL(strlink).v_char[i] == '%') + return; + if (strlength < 2 || SPEC_CVAL(strlink).v_char[strlength - 2] != '\n') + return; + + symbol *puts_sym = findSym(SymbolTab, NULL, "puts"); + + if (!puts_sym) + return; + + // Do the equivalent of + // DCL_ELEM (strsym->type)--; + // but in a way that works better with the reuse of string symbols + { + struct dbuf_s dbuf; + dbuf_init(&dbuf, strlength - 1); + wassert(dbuf_append(&dbuf, SPEC_CVAL(strlink).v_char, strlength - 1)); + ((char *)(dbuf_get_buf(&dbuf)))[strlength - 2] = 0; + + parm->opval.val = + stringToSymbol(rawStrVal(dbuf_get_buf(&dbuf), strlength - 1)); + dbuf_destroy(&dbuf); + + freeStringSymbol(strsym); + } + + func->opval.val->sym = puts_sym; + } + // Optimize strcpy() to memcpy(). + else if ((!strcmp(funcname, "strcpy") || + !strcmp(funcname, "__builtin_strcpy")) && + nparms == 2) { + ast *parm = parms->right; + + if (parm->type == EX_OP && parm->opval.op == CAST) + parm = parm->right; + + if (parm->type != EX_VALUE || !IS_ARRAY(parm->opval.val->type) || + !parm->opval.val->sym) + return; + + size_t strlength = DCL_ELEM(parm->opval.val->type); + symbol *strsym = parm->opval.val->sym; + sym_link *strlink = strsym->etype; + + if (!strsym->isstrlit || !strlink || !IS_SPEC(strlink) || + SPEC_NOUN(strlink) != V_CHAR) + return; + + for (size_t i = 0; i < strlength; i++) + if (!SPEC_CVAL(strlink).v_char[i]) { + strlength = i + 1; + break; + } + + size_t minlength; // Minimum string length for replacement. + // TODO:Check for other targets when memcpy() is a better choice than + // strcpy; + minlength = SIZE_MAX; + + if (strlength < minlength) + return; + + symbol *memcpy_sym = findSym( + SymbolTab, NULL, + !strcmp(funcname, "__builtin_strcpy") ? "__builtin_memcpy" : "memcpy"); + + if (!memcpy_sym) + return; + + ast *lengthparm = + newAst_VALUE(valCastLiteral(newIntLink(), strlength, strlength)); + decorateType(lengthparm, RESULT_TYPE_NONE); + ast *node = newAst_OP(PARAM); + + node->left = newNode( + CAST, newAst_LINK(copyLinkChain(FUNC_ARGS(memcpy_sym->type)->type)), + parm); + node->left->values.cast.implicitCast = 1; + node->left->lineno = parm->lineno; + node->left->filename = node->left->left->filename = parm->filename; + node->left = decorateType(node->left, RESULT_TYPE_GPTR); + + node->right = lengthparm; + node->decorated = 1; + parms->right = node; + func->opval.val->sym = memcpy_sym; + } +} + +/*--------------------------------------------------------------*/ +/* rewrite ast node as an operator node and rejoin any orphaned */ +/* side-effects with a comma operator */ +/*--------------------------------------------------------------*/ +static void rewriteAstNodeOp(ast *tree, int op, ast *left, ast *right) { + ast *oLeft = (tree->type == EX_OP) ? tree->left : NULL; + ast *oRight = (tree->type == EX_OP) ? tree->right : NULL; + + tree->type = EX_OP; + tree->opval.op = op; + tree->left = left; + tree->right = right; + tree->decorated = 0; + + rewriteAstJoinSideEffects(tree, oLeft, oRight); +} + +/*----------------------------------------------------------*/ +/* rewrite ast node as a value node and rejoin any orphaned */ +/* side-effects with a comma operator */ +/*----------------------------------------------------------*/ +static void rewriteAstNodeVal(ast *tree, value *val) { + ast *oLeft = (tree->type == EX_OP) ? tree->left : NULL; + ast *oRight = (tree->type == EX_OP) ? tree->right : NULL; + + tree->type = EX_VALUE; + tree->opval.val = val; + tree->left = NULL; + tree->right = NULL; + TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); + tree->decorated = 0; + + rewriteAstJoinSideEffects(tree, oLeft, oRight); +} + +/*-----------------------------------------------------------*/ +/* rewrite struct assignment "a = b" to something similar to */ +/* "__builtin_memcpy (&a, &b, sizeof (a)), a" or, if a has */ +/* side effects, "*(__builtin_memcpy (&a, &b, sizeof (a)))" */ +/*-----------------------------------------------------------*/ +ast *rewriteStructAssignment(ast *tree) { + /* prepare pointer to destination */ + ast *dest = newNode('&', tree->left, NULL); + copyAstLoc(dest, tree->left); + + /* prepare remaining arguments */ + ast *src = newNode('&', tree->right, NULL); + copyAstLoc(src, tree->right); + ast *size = newNode(SIZEOF, NULL, tree->left); + copyAstLoc(size, tree->left); + ast *srcsize = newNode(PARAM, src, size); + copyAstLoc(srcsize, tree); + ast *params = newNode(PARAM, dest, srcsize); + copyAstLoc(params, tree); + + /* create call to the appropriate memcpy function */ + ast *memcpy_ast = newAst_VALUE(symbolVal(memcpy_builtin)); + copyAstLoc(memcpy_ast, tree); + ast *call = newNode(CALL, memcpy_ast, params); + copyAstLoc(call, tree); + + /* assemble the result expression depending on side effects */ + ast *newTree; + if (hasSEFcalls(dest)) { + /* memcpy returns the dest pointer -> dereference it */ + newTree = newNode('*', call, NULL); + } else { + /* no side effects -> dereference dest pointer itself */ + ast *destderef = newNode('*', dest, NULL); + copyAstLoc(destderef, dest); + newTree = newNode(',', call, destderef); + } + + /* copy source location and return decorated result */ + copyAstLoc(newTree, tree); + return decorateType(newTree, RESULT_TYPE_OTHER); +} + /*--------------------------------------------------------------------*/ /* decorateType - compute type for this tree, also does type checking.*/ /* This is done bottom up, since type has to flow upwards. */ @@ -2827,7 +3285,14 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* array node */ /*----------------------------*/ case '[': - /* first check if this is an array or a pointer */ + /* Swap trees if right side is array or a pointer */ + if (IS_ARRAY(RTYPE(tree)) || IS_PTR(RTYPE(tree))) { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* check if this is an array or a pointer */ if ((!IS_ARRAY(LTYPE(tree))) && (!IS_PTR(LTYPE(tree)))) { werrorfl(tree->filename, tree->lineno, E_NEED_ARRAY_PTR, "[]"); goto errorTreeReturn; @@ -2855,8 +3320,11 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } RRVAL(tree) = 1; - COPYTYPE(TTYPE(tree), TETYPE(tree), LTYPE(tree)->next); - if (IS_PTR(LTYPE(tree)) && !IS_LITERAL(TETYPE(tree))) { + TTYPE(tree) = copyLinkChain(LTYPE(tree)->next); + TETYPE(tree) = getSpec(TTYPE(tree)); + + if (IS_PTR(LTYPE( + tree)) /* && !IS_LITERAL (TETYPE (tree)) caused bug #2850 */) { SPEC_SCLS(TETYPE(tree)) = sclsFromPtr(LTYPE(tree)); } @@ -3049,6 +3517,12 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { if (otree != tree) return decorateType(otree, RESULT_TYPE_NONE); + /* if right is a literal and has the same size with left, + then also sync their signess to avoid unecessary cast */ + if (IS_LITERAL(RTYPE(tree)) && + getSize(RTYPE(tree)) == getSize(LTYPE(tree))) + SPEC_USIGN(RTYPE(tree)) = SPEC_USIGN(LTYPE(tree)); + LRVAL(tree) = RRVAL(tree) = 1; TTYPE(tree) = @@ -3068,7 +3542,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } /* if bit field then error */ - if (IS_BITFIELD(tree->left->etype) || (IS_BITVAR(tree->left->etype))) { + if (IS_BITFIELD(tree->left->etype)) { werrorfl(tree->filename, tree->lineno, E_ILLEGAL_ADDR, "address of bit variable"); goto errorTreeReturn; @@ -3202,13 +3676,10 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = valBitwise(valFromType(LETYPE(tree)), - valFromType(RETYPE(tree)), tree->opval.op); - tree->right = tree->left = NULL; - TETYPE(tree) = tree->opval.val->etype; - TTYPE(tree) = tree->opval.val->type; - return tree; + rewriteAstNodeVal(tree, + valBitwise(valFromType(LETYPE(tree)), + valFromType(RETYPE(tree)), tree->opval.op)); + return decorateType(tree, resultType); } /* if left is a literal exchange left & right */ @@ -3252,6 +3723,11 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } } + /* if right is a literal and has the same size with left, + then also sync their signess to avoid unecessary cast */ + if (IS_LITERAL(RTYPE(tree)) && getSize(RTYPE(tree)) == getSize(LTYPE(tree))) + SPEC_USIGN(RTYPE(tree)) = SPEC_USIGN(LTYPE(tree)); + LRVAL(tree) = RRVAL(tree) = 1; TTYPE(tree) = @@ -3269,15 +3745,15 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { werrorfl(tree->filename, tree->lineno, E_INVALID_OP, "divide"); goto errorTreeReturn; } + /* check if div by zero */ + if (IS_LITERAL(RTYPE(tree)) && !IS_LITERAL(LTYPE(tree))) + checkZero(valFromType(RETYPE(tree))); /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = - valDiv(valFromType(LETYPE(tree)), valFromType(RETYPE(tree))); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal( + tree, valDiv(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)))); + return decorateType(tree, resultType); } LRVAL(tree) = RRVAL(tree) = 1; @@ -3286,6 +3762,46 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { getSpec(TTYPE(tree) = computeType(LTYPE(tree), RTYPE(tree), resultType, tree->opval.op)); + /* if right is a literal and */ + /* left is also a division by a literal then */ + /* rearrange the tree */ +#if 0 + /* This converts (a/b)/c into a/(b*c)/1, where b and c are literals. */ + /* Algebraically, this is fine, but may fail as an optimization if */ + /* b*c overflows or causes the expression to have a different resultant */ + /* type. I don't think it's worth the effort to sort out the cases of */ + /* when this is safe or not safe, so I am just going to leave this */ + /* disabled. -- EEP -- 15 Nov 2012 */ + if (IS_LITERAL (RTYPE (tree)) + /* avoid infinite loop */ + && (TYPE_TARGET_ULONG) ulFromVal (tree->right->opval.val) != 1) + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "/"); + if (litTree) + { + if (IS_LITERAL (RTYPE (litTree))) + { + /* foo_div */ + DEBUG_CF ("div r") litTree->right = newNode ('*', litTree->right, copyAst (tree->right)); + litTree->right->filename = tree->filename; + litTree->right->lineno = tree->lineno; + + tree->right->opval.val = constCharVal (1); + decorateType (parent, resultType); + } + else + { + /* litTree->left is literal: no gcse possible. + We can't call decorateType(parent, RESULT_TYPE_NONE), because + this would cause an infinit loop. */ + parent->decorated = 1; + decorateType (litTree, resultType); + } + } + } +#endif + return tree; /*------------------------------------------------------------------*/ @@ -3303,15 +3819,15 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { fprintf(stderr, "\n"); goto errorTreeReturn; } + /* check if div by zero */ + if (IS_LITERAL(RTYPE(tree)) && !IS_LITERAL(LTYPE(tree))) + checkZero(valFromType(RETYPE(tree))); /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = - valMod(valFromType(LETYPE(tree)), valFromType(RETYPE(tree))); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal( + tree, valMod(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)))); + return decorateType(tree, resultType); } LRVAL(tree) = RRVAL(tree) = 1; TETYPE(tree) = @@ -3342,35 +3858,10 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { TTYPE(tree) = copyLinkChain(LTYPE(tree)->next); TETYPE(tree) = getSpec(TTYPE(tree)); /* adjust the storage class */ - switch (DCL_TYPE(tree->left->ftype)) { - case POINTER: - SPEC_SCLS(TETYPE(tree)) = S_DATA; - break; - case FPOINTER: - SPEC_SCLS(TETYPE(tree)) = S_XDATA; - break; - case CPOINTER: - SPEC_SCLS(TETYPE(tree)) = S_CODE; - break; - case GPOINTER: - SPEC_SCLS(TETYPE(tree)) = 0; - break; - case PPOINTER: - SPEC_SCLS(TETYPE(tree)) = S_XSTACK; - break; - case IPOINTER: - SPEC_SCLS(TETYPE(tree)) = S_IDATA; - break; - case EEPPOINTER: - SPEC_SCLS(TETYPE(tree)) = S_EEPROM; - break; - case UPOINTER: - SPEC_SCLS(TETYPE(tree)) = 0; - break; - case ARRAY: - case FUNCTION: - break; - } + if (DCL_TYPE(tree->left->ftype) != ARRAY && + DCL_TYPE(tree->left->ftype) != FUNCTION) + SPEC_SCLS(TETYPE(tree)) = sclsFromPtr(tree->left->ftype); + return tree; } @@ -3386,12 +3877,9 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = - valMult(valFromType(LETYPE(tree)), valFromType(RETYPE(tree))); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal( + tree, valMult(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)))); + return decorateType(tree, resultType); } /* if left is a literal exchange left & right */ @@ -3417,8 +3905,15 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } LRVAL(tree) = RRVAL(tree) = 1; - tree->left = addCast(tree->left, resultTypeProp, FALSE); - tree->right = addCast(tree->right, resultTypeProp, FALSE); + + { // cast happen only if both left and right can be casted to result type + ast *l = addCast(tree->left, resultTypeProp, FALSE); + ast *r = addCast(tree->right, resultTypeProp, FALSE); + if (l != tree->left && r != tree->right) { + tree->left = l; + tree->right = r; + } + } TETYPE(tree) = getSpec(TTYPE(tree) = computeType(LTYPE(tree), RTYPE(tree), resultType, tree->opval.op)); @@ -3477,14 +3972,11 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; tree->left = addCast(tree->left, resultTypeProp, TRUE); tree->right = addCast(tree->right, resultTypeProp, TRUE); - tree->opval.val = - valPlus(valFromType(LETYPE(tree)), valFromType(RETYPE(tree))); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal( + tree, valPlus(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)))); + return decorateType(tree, resultType); } /* if the right is a pointer or left is a literal @@ -3531,6 +4023,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } LRVAL(tree) = RRVAL(tree) = 1; + /* if the left is a pointer */ if (IS_PTR(LTYPE(tree)) || IS_AGGREGATE(LTYPE(tree))) TETYPE(tree) = getSpec(TTYPE(tree) = LTYPE(tree)); @@ -3604,21 +4097,19 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; tree->left = addCast(tree->left, resultTypeProp, TRUE); tree->right = addCast(tree->right, resultTypeProp, TRUE); - tree->opval.val = - valMinus(valFromType(LETYPE(tree)), valFromType(RETYPE(tree))); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal( + tree, valMinus(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)))); + return decorateType(tree, resultType); } /* if the left & right are equal then zero */ - if (isAstEqual(tree->left, tree->right)) { + if (!hasSEFcalls(tree->left) && !hasSEFcalls(tree->right) && + isAstEqual(tree->left, tree->right)) { tree->type = EX_VALUE; tree->left = tree->right = NULL; - tree->opval.val = constCharVal(0); + tree->opval.val = constVal("0"); TETYPE(tree) = TTYPE(tree) = tree->opval.val->type; return tree; } @@ -3719,7 +4210,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* optimize bit-result, even if we optimize a buggy source */ tree->type = EX_VALUE; - tree->opval.val = constCharVal(1); + tree->opval.val = constBoolVal(1); } else tree->left = addCast(tree->left, resultTypeProp, TRUE); LRVAL(tree) = 1; @@ -3733,35 +4224,36 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { case '!': /* can be pointer */ if (!IS_ARITHMETIC(LTYPE(tree)) && !IS_PTR(LTYPE(tree)) && - !IS_ARRAY(LTYPE(tree))) { + !IS_FUNC(LTYPE(tree)) && !IS_ARRAY(LTYPE(tree))) { werrorfl(tree->filename, tree->lineno, E_UNARY_OP, tree->opval.op); goto errorTreeReturn; } /* if left is another '!' */ - if (IS_AST_NOT_OPER(tree->left)) { - if ((resultType == RESULT_TYPE_IFX) || (resultType == RESULT_TYPE_BOOL)) { - /* replace double '!!X' by 'X' */ - return tree->left->left; - } - /* remove double '!!X' by 'X ? 1 : 0' */ - tree->opval.op = '?'; - tree->left = tree->left->left; - tree->right = newNode(':', newAst_VALUE(constCharVal(1)), - newAst_VALUE(constCharVal(0))); - tree->right->filename = tree->filename; - tree->right->lineno = tree->lineno; - tree->decorated = 0; - return decorateType(tree, resultType); - } +#if 0 /* Disabled optimization due to bugs #2548, #2551. */ + if (IS_AST_NOT_OPER (tree->left)) + { + if (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) + { + /* replace double '!!X' by 'X' */ + return tree->left->left; + } + + /* remove double '!!X' by 'X ? 1 : 0' */ /* TODO: Casts to _Bools tend to result in far more efficient code than '?' */ + tree->opval.op = '?'; + tree->left = tree->left->left; + tree->right = newNode (':', newAst_VALUE (constBoolVal (1)), newAst_VALUE (constBoolVal (0))); + tree->right->filename = tree->filename; + tree->right->lineno = tree->lineno; + tree->decorated = 0; + return decorateType (tree, resultType); + } +#endif /* if left is a literal then do it */ if (IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = valNot(valFromType(LETYPE(tree))); - tree->left = NULL; - TETYPE(tree) = TTYPE(tree) = tree->opval.val->type; - return tree; + rewriteAstNodeVal(tree, valNot(valFromType(LETYPE(tree)))); + return decorateType(tree, resultType); } LRVAL(tree) = 1; TTYPE(tree) = TETYPE(tree) = @@ -3815,13 +4307,10 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = - valShift(valFromType(LETYPE(tree)), valFromType(RETYPE(tree)), - (tree->opval.op == LEFT_OP ? 1 : 0)); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal(tree, valShift(valFromType(LETYPE(tree)), + valFromType(RETYPE(tree)), + (tree->opval.op == LEFT_OP ? 1 : 0))); + return decorateType(tree, resultType); } /* see if this is a GETBYTE operation if yes @@ -3855,11 +4344,22 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { SPEC_SCLS(TTYPE(tree)) &= ~S_LITERAL; } + if (IS_LITERAL(RTYPE(tree)) && + floatFromVal(valFromType(RETYPE(tree))) < 0) { + werrorfl(tree->filename, tree->lineno, W_SHIFT_NEGATIVE, + (tree->opval.op == LEFT_OP ? "left" : "right")); + /* Change shift op to comma op and replace the right operand with 0. */ + /* This preserves the left operand in case there were side-effects. */ + tree->opval.op = ','; + tree->right->opval.val = constVal("0"); + TETYPE(tree) = TTYPE(tree) = tree->right->opval.val->type; + return tree; + } /* if only the right side is a literal & we are shifting more than size of the left operand then zero */ - if (IS_LITERAL(RTYPE(tree)) && - ((TYPE_TARGET_ULONG)ulFromVal(valFromType(RETYPE(tree)))) >= - (getSize(TETYPE(tree)) * 8)) { + else if (IS_LITERAL(RTYPE(tree)) && + ((TYPE_TARGET_ULONG)ulFromVal(valFromType(RETYPE(tree)))) >= + (getSize(TETYPE(tree)) * 8)) { if (tree->opval.op == LEFT_OP || (tree->opval.op == RIGHT_OP && SPEC_USIGN(LETYPE(tree)))) { werrorfl(tree->filename, tree->lineno, W_SHIFT_CHANGED, @@ -3867,7 +4367,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* Change shift op to comma op and replace the right operand with 0. */ /* This preserves the left operand in case there were side-effects. */ tree->opval.op = ','; - tree->right->opval.val = constCharVal(0); + tree->right->opval.val = constVal("0"); TETYPE(tree) = TTYPE(tree) = tree->right->opval.val->type; return tree; } @@ -3907,12 +4407,143 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* implicitly point to constants -- make this explicit */ CodePtrPointsToConst(LTYPE(tree)); +#if 0 + /* if the right is a literal replace the tree */ + if (IS_LITERAL (RETYPE (tree))) + { + if (!IS_PTR (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), floatFromVal (valFromType (RETYPE (tree)))); + tree->left = NULL; + tree->right = NULL; + TTYPE (tree) = tree->opval.val->type; + tree->values.cast.literalFromCast = 1; + } + else if (IS_GENPTR (LTYPE (tree)) && !IS_PTR (RTYPE (tree)) && ((int) ulFromVal (valFromType (RETYPE (tree)))) != 0) /* special case of NULL */ + { + sym_link *rest = LTYPE (tree)->next; + werrorfl (tree->filename, tree->lineno, W_LITERAL_GENERIC); + TTYPE (tree) = newLink (DECLARATOR); + DCL_TYPE (TTYPE (tree)) = FPOINTER; + TTYPE (tree)->next = rest; + tree->left->opval.lnk = TTYPE (tree); + LRVAL (tree) = 1; + } + else + { + TTYPE (tree) = LTYPE (tree); + LRVAL (tree) = 1; + } + } + else + { + TTYPE (tree) = LTYPE (tree); + LRVAL (tree) = 1; + } +#else +#if 0 // this is already checked, now this could be explicit + /* if pointer to struct then check names */ + if (IS_PTR (LTYPE (tree)) && IS_STRUCT (LTYPE (tree)->next) && + IS_PTR (RTYPE (tree)) && IS_STRUCT (RTYPE (tree)->next) && + strcmp (SPEC_STRUCT (LETYPE (tree))->tag, SPEC_STRUCT (RETYPE (tree))->tag)) + { + werrorfl (tree->filename, tree->lineno, W_CAST_STRUCT_PTR, SPEC_STRUCT (RETYPE (tree))->tag, + SPEC_STRUCT (LETYPE (tree))->tag); + } +#endif +#if 0 // disabled to fix bug 2941749 + if (IS_ADDRESS_OF_OP (tree->right) + && IS_AST_SYM_VALUE (tree->right->left) && SPEC_ABSA (AST_SYMBOL (tree->right->left)->etype)) + { + symbol *sym = AST_SYMBOL (tree->right->left); + unsigned int gptype = 0; + unsigned int addr = SPEC_ADDR (sym->etype); + + if (IS_GENPTR (LTYPE (tree)) && ((GPTRSIZE > FARPTRSIZE) || TARGET_IS_PIC16)) + { + switch (SPEC_SCLS (sym->etype)) + { + case S_CODE: + gptype = GPTYPE_CODE; + break; + case S_XDATA: + gptype = GPTYPE_FAR; + break; + case S_DATA: + case S_IDATA: + gptype = GPTYPE_NEAR; + break; + case S_PDATA: + gptype = GPTYPE_XSTACK; + break; + default: + gptype = 0; + if (TARGET_IS_PIC16 && (SPEC_SCLS (sym->etype) == S_FIXED)) + gptype = GPTYPE_NEAR; + } + addr |= gptype << (8 * (GPTRSIZE - 1)); + } + + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), addr); + TTYPE (tree) = tree->opval.val->type; + TETYPE (tree) = getSpec (TTYPE (tree)); + tree->left = NULL; + tree->right = NULL; + tree->values.cast.literalFromCast = 1; + return tree; + } +#endif + /* if the right is a literal replace the tree */ if (IS_LITERAL(RETYPE(tree))) { +#if 0 + if (IS_PTR (LTYPE (tree)) && !IS_GENPTR (LTYPE (tree))) + { + /* rewrite (type *)litaddr + as &temp + and define type at litaddr temp + (but only if type's storage class is not generic) + */ + ast *newTree = newNode ('&', NULL, NULL); + symbol *sym; + + TTYPE (newTree) = LTYPE (tree); + TETYPE (newTree) = getSpec (LTYPE (tree)); + + /* define a global symbol at the casted address */ + sym = newSymbol (genSymName (0), 0); + sym->type = LTYPE (tree)->next; + if (!sym->type) + sym->type = newLink (V_VOID); + sym->etype = getSpec (sym->type); + SPEC_SCLS (sym->etype) = sclsFromPtr (LTYPE (tree)); + sym->lineDef = tree->lineno; + sym->cdef = 1; + sym->isref = 1; + SPEC_STAT (sym->etype) = 1; + SPEC_ADDR (sym->etype) = floatFromVal (valFromType (RTYPE (tree))); + SPEC_ABSA (sym->etype) = 1; + addSym (SymbolTab, sym, sym->name, 0, 0, 0); + allocGlobal (sym); + + newTree->left = newAst_VALUE (symbolVal (sym)); + newTree->left->filename = tree->filename; + newTree->left->lineno = tree->lineno; + LTYPE (newTree) = sym->type; + LETYPE (newTree) = sym->etype; + LLVAL (newTree) = 1; + LRVAL (newTree) = 0; + TLVAL (newTree) = 1; + return newTree; + } +#endif if (!IS_PTR(LTYPE(tree))) { tree->type = EX_VALUE; - tree->opval.val = - valCastLiteral(LTYPE(tree), floatFromVal(valFromType(RTYPE(tree)))); + tree->opval.val = valCastLiteral( + LTYPE(tree), floatFromVal(valFromType(RTYPE(tree))), + (TYPE_TARGET_ULONGLONG)ullFromVal(valFromType(RTYPE(tree)))); TTYPE(tree) = tree->opval.val->type; tree->left = NULL; tree->right = NULL; @@ -3920,11 +4551,12 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { TETYPE(tree) = getSpec(TTYPE(tree)); return tree; } else { - unsigned long gpVal = 0; + unsigned long long gpVal = 0; int size = getSize(LTYPE(tree)); - unsigned long mask = - (size >= sizeof(long)) ? 0xffffffff : (1ul << (size * 8)) - 1; - unsigned long pVal = ulFromVal(valFromType(RTYPE(tree))) & mask; + unsigned long long mask = (size >= sizeof(long long)) + ? 0xffffffffffffffffull + : (1ull << (size * 8)) - 1; + unsigned long long pVal = ullFromVal(valFromType(RTYPE(tree))) & mask; /* if casting literal specific pointer to generic pointer */ if (IS_GENPTR(LTYPE(tree)) && IS_PTR(RTYPE(tree)) && @@ -3937,10 +4569,12 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { gpVal &= mask; } } - checkPtrCast(LTYPE(tree), RTYPE(tree), tree->values.cast.implicitCast); + checkPtrCast(LTYPE(tree), RTYPE(tree), tree->values.cast.implicitCast, + !ullFromVal(valFromType(RTYPE(tree)))); LRVAL(tree) = 1; tree->type = EX_VALUE; - tree->opval.val = valCastLiteral(LTYPE(tree), gpVal | pVal); + tree->opval.val = + valCastLiteral(LTYPE(tree), gpVal | pVal, gpVal | pVal); TTYPE(tree) = tree->opval.val->type; tree->left = NULL; tree->right = NULL; @@ -3949,7 +4583,8 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { return tree; } } - checkPtrCast(LTYPE(tree), RTYPE(tree), tree->values.cast.implicitCast); + checkPtrCast(LTYPE(tree), RTYPE(tree), tree->values.cast.implicitCast, + FALSE); if (IS_GENPTR(LTYPE(tree)) && (resultType != RESULT_TYPE_GPTR)) { if (IS_PTR(RTYPE(tree)) && !IS_GENPTR(RTYPE(tree))) DCL_TYPE(LTYPE(tree)) = DCL_TYPE(RTYPE(tree)); @@ -3959,6 +4594,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { TTYPE(tree) = LTYPE(tree); LRVAL(tree) = 1; +#endif TETYPE(tree) = getSpec(TTYPE(tree)); return tree; @@ -3984,12 +4620,10 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = valLogicAndOr(valFromType(LTYPE(tree)), - valFromType(RTYPE(tree)), tree->opval.op); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal(tree, valLogicAndOr(valFromType(LETYPE(tree)), + valFromType(RETYPE(tree)), + tree->opval.op)); + return decorateType(tree, resultType); } LRVAL(tree) = RRVAL(tree) = 1; TTYPE(tree) = TETYPE(tree) = @@ -4028,7 +4662,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { tree->right = s; } if (compareType(LTYPE(tree), RTYPE(tree)) == 0) { - werrorfl(tree->filename, tree->lineno, E_COMPARE_OP); + werrorfl(tree->filename, tree->lineno, E_INCOMPAT_TYPES); fprintf(stderr, "comparing type "); printTypeChain(LTYPE(tree), stderr); fprintf(stderr, " to type "); @@ -4040,21 +4674,41 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* else they should be promotable to one another */ else { if (!((IS_PTR(LTYPE(tree)) && IS_LITERAL(RTYPE(tree))) || - (IS_PTR(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))))) + (IS_PTR(RTYPE(tree)) && IS_LITERAL(LTYPE(tree))) || + ((tree->opval.op == EQ_OP || tree->opval.op == NE_OP) && + ((IS_FUNC(LTYPE(tree)) && IS_LITERAL(RTYPE(tree)) && + !ullFromVal(valFromType(RTYPE(tree)))) || + (IS_FUNC(RTYPE(tree)) && IS_LITERAL(LTYPE(tree)) && + !ullFromVal(valFromType(LTYPE(tree)))))))) if (compareType(LTYPE(tree), RTYPE(tree)) == 0) { - werrorfl(tree->filename, tree->lineno, E_COMPARE_OP); - fprintf(stderr, "comparing type "); - printTypeChain(LTYPE(tree), stderr); - fprintf(stderr, " to type "); - printTypeChain(RTYPE(tree), stderr); - fprintf(stderr, "\n"); - goto errorTreeReturn; + if (compareType(RTYPE(tree), LTYPE(tree)) != 0) { + struct ast *s = tree->left; + tree->left = tree->right; + tree->right = s; + if (tree->opval.op == '>') + tree->opval.op = '<'; + else if (tree->opval.op == '>') + tree->opval.op = '<'; + else if (tree->opval.op == LE_OP) + tree->opval.op = GE_OP; + else if (tree->opval.op == GE_OP) + tree->opval.op = LE_OP; + } else { + werrorfl(tree->filename, tree->lineno, E_INCOMPAT_TYPES); + fprintf(stderr, "comparing type "); + printTypeChain(LTYPE(tree), stderr); + fprintf(stderr, " to type "); + printTypeChain(RTYPE(tree), stderr); + fprintf(stderr, "\n"); + goto errorTreeReturn; + } } } { CCR_RESULT ccr_result = CCR_OK; + ast *newResult; /* if left is integral and right is literal then check constant range */ @@ -4071,9 +4725,17 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { case CCR_ALWAYS_FALSE: werrorfl(tree->filename, tree->lineno, W_COMP_RANGE, ccr_result == CCR_ALWAYS_TRUE ? "true" : "false"); - return decorateType(newAst_VALUE(constCharVal(( - unsigned char)(ccr_result == CCR_ALWAYS_TRUE))), - resultType); + newResult = newAst_VALUE( + constBoolVal((unsigned char)(ccr_result == CCR_ALWAYS_TRUE))); + /* If there are side effects, join the non-literal side */ + /* to the boolean result with a comma operator */ + if (hasSEFcalls(tree)) { + if (!IS_LITERAL(LTYPE(tree))) + newResult = newNode(',', tree->left, newResult); + else + newResult = newNode(',', tree->right, newResult); + } + return decorateType(newResult, resultType); case CCR_OK: default: break; @@ -4090,10 +4752,11 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { return tree->left; } - /* (unsigned value) ? 1 : 0 */ + /* (unsigned value) ? 1 : 0 */ /* TODO: Casts to _Bools tend to result in + far more efficient code than '?' */ tree->opval.op = '?'; tree->right = - newNode(':', newAst_VALUE(constCharVal(1)), tree->right); /* val 0 */ + newNode(':', newAst_VALUE(constBoolVal(1)), tree->right); /* val 0 */ tree->right->filename = tree->filename; tree->right->lineno = tree->lineno; tree->right->left->filename = tree->filename; @@ -4107,10 +4770,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { floatFromVal(valFromType(LTYPE(tree))) == 0 && tree->opval.op == EQ_OP && (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) { - tree->opval.op = '!'; - tree->left = tree->right; - tree->right = NULL; - tree->decorated = 0; + rewriteAstNodeOp(tree, '!', tree->right, NULL); return decorateType(tree, resultType); } @@ -4119,21 +4779,26 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { floatFromVal(valFromType(RTYPE(tree))) == 0 && tree->opval.op == EQ_OP && (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) { - tree->opval.op = '!'; - tree->right = NULL; - tree->decorated = 0; + rewriteAstNodeOp(tree, '!', tree->left, NULL); + return decorateType(tree, resultType); + } + + /* 'ifx (op == 1)' -> 'ifx (op)' for bool */ + if (IS_LITERAL(RETYPE(tree)) && + floatFromVal(valFromType(RTYPE(tree))) == 1 && + IS_BOOLEAN(LETYPE(tree)) && tree->opval.op == EQ_OP && + (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) { + tree = tree->left; return decorateType(tree, resultType); } /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL(RETYPE(tree)) && IS_LITERAL(LETYPE(tree))) { - tree->type = EX_VALUE; - tree->opval.val = valCompare(valFromType(LETYPE(tree)), - valFromType(RETYPE(tree)), tree->opval.op); - tree->right = tree->left = NULL; - TETYPE(tree) = getSpec(TTYPE(tree) = tree->opval.val->type); - return tree; + rewriteAstNodeVal(tree, + valCompare(valFromType(LETYPE(tree)), + valFromType(RETYPE(tree)), tree->opval.op)); + return decorateType(tree, resultType); } /* if one is 'signed char ' and the other one is 'unsigned char' */ @@ -4219,7 +4884,7 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /*----------------------------*/ /* sizeof */ /*----------------------------*/ - case SIZEOF: /* evaluate wihout code generation */ + case SIZEOF: /* evaluate without code generation */ { /* change the type to a integer */ struct dbuf_s dbuf; @@ -4337,10 +5002,26 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { but faster to do it here. If done before decorating tree->right this can save generating unused const strings. */ if (IS_LITERAL(LTYPE(tree))) { + ast *heir; + + ++noAlloc; + tree->right = decorateType(tree->right, resultTypeProp); + --noAlloc; + if (((int)ulFromVal(valFromType(LETYPE(tree)))) != 0) - return decorateType(tree->right->left, resultTypeProp); + heir = tree->right->left; + else + heir = tree->right->right; + + heir = decorateType(heir, resultTypeProp); + if (IS_LITERAL(TETYPE(heir))) + TTYPE(heir) = + valRecastLitVal(TTYPE(tree->right), valFromType(TETYPE(heir))) + ->type; else - return decorateType(tree->right->right, resultTypeProp); + TTYPE(heir) = TTYPE(tree->right); + TETYPE(heir) = getSpec(TTYPE(heir)); + return heir; } tree->right = decorateType(tree->right, resultTypeProp); @@ -4376,8 +5057,64 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { TETYPE(tree) = getSpec(TTYPE(tree)); return tree; + case GENERIC: { + sym_link *type = tree->left->ftype; + ast *assoc_list; + ast *default_expr = 0; + ast *found_expr = 0; + + for (assoc_list = tree->right; assoc_list; assoc_list = assoc_list->left) { + ast *const assoc = assoc_list->right; + if (!assoc->left) { + if (default_expr) { + werror(E_MULTIPLE_DEFAULT_IN_GENERIC); + goto errorTreeReturn; + } + default_expr = assoc->right; + } else { + sym_link *assoc_type; + wassert(IS_AST_LINK(assoc->left)); + assoc_type = assoc->left->opval.lnk; + checkTypeSanity(assoc_type, "_Generic"); + + if (compareType(type, assoc->left->opval.lnk) > 0 && + !(SPEC_NOUN(type) == V_CHAR && + type->select.s.b_implicit_sign != + assoc->left->opval.lnk->select.s.b_implicit_sign)) { + if (found_expr) { + werror(E_MULTIPLE_MATCHES_IN_GENERIC); + goto errorTreeReturn; + } + found_expr = assoc->right; + } + } + } + if (!found_expr) + found_expr = default_expr; + + if (!found_expr) { + werror(E_NO_MATCH_IN_GENERIC); + goto errorTreeReturn; + } + + tree = found_expr; + } + return tree; + + case GENERIC_ASSOC_LIST: + return tree; + + case GENERIC_ASSOCIATION: + return tree; + case ':': - /* if they don't match we have a problem */ + if ((compareType(LTYPE(tree), RTYPE(tree)) == 0) && + (compareType(RTYPE(tree), LTYPE(tree)) == 0)) { + if (IS_PTR(LTYPE(tree)) && !IS_GENPTR(LTYPE(tree))) + DCL_TYPE(LTYPE(tree)) = GPOINTER; + if (IS_PTR(RTYPE(tree)) && !IS_GENPTR(RTYPE(tree))) + DCL_TYPE(RTYPE(tree)) = GPOINTER; + } if ((compareType(LTYPE(tree), RTYPE(tree)) == 0) && (compareType(RTYPE(tree), LTYPE(tree)) == 0) && @@ -4385,12 +5122,13 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { !(IS_ARRAY(RTYPE(tree)) && IS_INTEGRAL(LTYPE(tree)))) { werrorfl(tree->filename, tree->lineno, E_TYPE_MISMATCH, "conditional operator", " "); - goto errorTreeReturn; + printFromToType(RTYPE(tree), LTYPE(tree)); } TTYPE(tree) = computeType(LTYPE(tree), RTYPE(tree), resultType, tree->opval.op); TETYPE(tree) = getSpec(TTYPE(tree)); + return tree; #if 0 // assignment operators are converted by the parser @@ -4525,9 +5263,9 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* straight assignemnt */ /*----------------------------*/ case '=': - /* cannot be an aggregate */ - if (IS_AGGREGATE(LTYPE(tree))) { - werrorfl(tree->filename, tree->lineno, E_AGGR_ASSIGN); + /* cannot be an array */ + if (IS_ARRAY(LTYPE(tree))) { + werrorfl(tree->filename, tree->lineno, E_ARRAY_ASSIGN); goto errorTreeReturn; } @@ -4555,8 +5293,12 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } TETYPE(tree) = getSpec(TTYPE(tree) = LTYPE(tree)); - RRVAL(tree) = 1; - LLVAL(tree) = 1; + if (IS_STRUCT(LTYPE(tree))) + tree = rewriteStructAssignment(tree); + else { + RRVAL(tree) = 1; + LLVAL(tree) = 1; + } if (!tree->initMode) { if (IS_CONSTANT(LTYPE(tree))) werrorfl(tree->filename, tree->lineno, E_CODE_WRITE, "="); @@ -4584,6 +5326,9 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { /* function call */ /*----------------------------*/ case CALL: + if (IFFUNC_ISCRITICAL(LTYPE(tree)) && + (inCriticalFunction || inCriticalBlock)) + werror(E_INVALID_CRITICAL); /* undo any explicit pointer dereference; PCALL will handle it instead */ if (IS_FUNC(LTYPE(tree)) && tree->left->type == EX_OP) { @@ -4609,15 +5354,18 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { } else functype = LTYPE(tree); + if (tree->right && tree->right->reversed) + reverseParms(tree->right, 0); + if (processParms(tree->left, FUNC_ARGS(functype), &tree->right, - &parmNumber, TRUE)) { + &parmNumber, TRUE)) goto errorTreeReturn; - } - if ((options.stackAuto || IFFUNC_ISREENT(functype)) && - !IFFUNC_ISBUILTIN(functype)) { - reverseParms(tree->right); - } + if (!optimize.noStdLibCall) + optStdLibCall(tree, resultType); + + if (options.stackAuto && !IFFUNC_ISBUILTIN(functype)) + reverseParms(tree->right, 1); TTYPE(tree) = copyLinkChain(functype->next); TETYPE(tree) = getSpec(TTYPE(tree)); @@ -4648,6 +5396,8 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { newNode(CAST, newAst_LINK(copyLinkChain(currFunc->type->next)), tree->right); tree->right->values.cast.implicitCast = 1; + tree->right->lineno = tree->right->left->lineno = tree->lineno; + tree->right->filename = tree->right->left->filename = tree->filename; tree->right = decorateType(tree->right, IS_GENPTR(currFunc->type->next) ? RESULT_TYPE_GPTR : RESULT_TYPE_NONE); @@ -4734,7 +5484,11 @@ ast *decorateType(ast *tree, RESULT_TYPE resultType) { case PARAM: werrorfl(tree->filename, tree->lineno, E_INTERNAL_ERROR, __FILE__, __LINE__, "node PARAM shouldn't be processed here"); - /* but in processParams() */ + /* but in processParms() */ + return tree; + case INLINEASM: + formatInlineAsm(tree->values.inlineasm); + TTYPE(tree) = TETYPE(tree) = NULL; return tree; default: TTYPE(tree) = TETYPE(tree) = NULL; @@ -4895,9 +5649,11 @@ ast *createBlock(symbol *decl, ast *body) { ex = newNode(BLOCK, NULL, body); ex->values.sym = decl; - ex->level++; + ex->level += LEVEL_UNIT; ex->filename = NULL; ex->lineno = 0; + if (body) + ex->block = body->block; return ex; } @@ -5385,6 +6141,7 @@ ast *optimizeGetWord(ast *tree, RESULT_TYPE resultType) { ast *count = NULL; expr = isShiftRightLitVal_BitAndLitVal(tree); + if (expr) { i = AST_ULONG_VALUE(tree->left->right); count = tree->left->right; @@ -5633,17 +6390,17 @@ static ast *optimizeCompare(ast *root) { /* if left & right are the same then depending of the operation do */ - if (isAstEqual(root->left, root->right)) { + if (isAstEqual(root->left, root->right) && !hasSEFcalls(root)) { switch (root->opval.op) { case '>': case '<': case NE_OP: - optExpr = newAst_VALUE(constCharVal(0)); + optExpr = newAst_VALUE(constBoolVal(0)); break; case GE_OP: case LE_OP: case EQ_OP: - optExpr = newAst_VALUE(constCharVal(1)); + optExpr = newAst_VALUE(constBoolVal(1)); break; } @@ -5741,11 +6498,9 @@ void addSymToBlock(symbol *sym, ast *tree) { /* processRegParms - do processing for register parameters */ /*-----------------------------------------------------------------*/ static void processRegParms(value *args, ast *body) { - while (args) { - if (IS_REGPARM(args->etype)) + for (; args; args = args->next) + if (IS_REGPARM(args->etype) && args->sym) addSymToBlock(args->sym, body); - args = args->next; - } } /*-----------------------------------------------------------------*/ @@ -5769,9 +6524,9 @@ DEFSETFUNC(resetParmKey) { static void fixupInlineLabel(symbol *sym) { struct dbuf_s dbuf; - dbuf_init(&dbuf, 128); + dbuf_init(&dbuf, SDCC_SYMNAME_MAX + 1); dbuf_printf(&dbuf, "%s_%d", sym->name, inlineState.count); - strncpyz(sym->name, dbuf_c_str(&dbuf), SDCC_NAME_MAX); + strncpyz(sym->name, dbuf_c_str(&dbuf), SDCC_SYMNAME_MAX); dbuf_destroy(&dbuf); } @@ -5787,13 +6542,13 @@ static void copyAstLoc(ast *dest, ast *src) { dest->seqPoint = src->seqPoint; } -static void fixupInline(ast *tree, int level); +static void fixupInline(ast *tree, long level); /*-----------------------------------------------------------------*/ -/* expandInlineFuncsInDeclarators - replace calls to inline */ -/* functions with the function itself */ +/* fixupInlineInDeclarators - recursively perform various fixups */ +/* on an inline function tree */ /*-----------------------------------------------------------------*/ -static void fixupInlineInDeclarators(struct initList *ival, int level) { +static void fixupInlineInDeclarators(struct initList *ival, long level) { for (; ival; ival = ival->next) { switch (ival->type) { case INIT_NODE: @@ -5811,7 +6566,7 @@ static void fixupInlineInDeclarators(struct initList *ival, int level) { /* to take into account that it is no longer a */ /* stand-alone function. */ /*-----------------------------------------------------------------*/ -static void fixupInline(ast *tree, int level) { +static void fixupInline(ast *tree, long level) { int savedBlockno = currBlockno; if (IS_AST_OP(tree) && (tree->opval.op == BLOCK)) { @@ -5822,7 +6577,7 @@ static void fixupInline(ast *tree, int level) { btree_add_child(currBlockno, ++blockNo); thisBlockBlockno = blockNo; - level++; + level += LEVEL_UNIT; /* Add any declared variables back into the symbol table */ for (decls = tree->values.sym; decls; decls = decls->next) { @@ -5871,8 +6626,9 @@ static void fixupInline(ast *tree, int level) { ast *gotoTree; if (inlineState.retsym && tree->right) { - assignTree = newNode('=', newAst_VALUE(symbolVal(inlineState.retsym)), - tree->right); + assignTree = + newNode('=', newAst_VALUE(symbolVal(copySymbol(inlineState.retsym))), + tree->right); copyAstLoc(assignTree, tree); } @@ -5928,7 +6684,7 @@ static void fixupInline(ast *tree, int level) { } if (IS_AST_OP(tree) && (tree->opval.op == BLOCK)) { - level--; + level -= LEVEL_UNIT; currBlockno = savedBlockno; } } @@ -5968,7 +6724,7 @@ static void inlineAddDecl(symbol *sym, ast *block, int addSymTab, int toFront) { /*-----------------------------------------------------------------*/ /* inlineTempVar - create a temporary variable for inlining */ /*-----------------------------------------------------------------*/ -static symbol *inlineTempVar(sym_link *type, int level) { +static symbol *inlineTempVar(sym_link *type, long level) { symbol *sym; sym = newSymbol(genSymName(level), level); @@ -5991,42 +6747,32 @@ static symbol *inlineTempVar(sym_link *type, int level) { } /*-----------------------------------------------------------------*/ -/* inlineFindParmRecurse - recursive function for inlineFindParm */ +/* inlineFindParm - search an ast tree of parameters to find one */ +/* at a particular index (0=first parameter). */ +/* Returns 0 if not found. */ /*-----------------------------------------------------------------*/ -static ast *inlineFindParmRecurse(ast *parms, int *index) { +static ast *inlineFindParm(ast *parms, int index) { if (!parms) - return NULL; + return 0; if (parms->type == EX_OP && parms->opval.op == PARAM) { ast *p; - p = inlineFindParmRecurse(parms->left, index); - if (p) + if (p = inlineFindParm(parms->left, index)) return p; - p = inlineFindParmRecurse(parms->right, index); - if (p) + + if (p = inlineFindParm(parms->right, index - 1)) return p; } - if (!*index) - return parms; - (*index)--; - return NULL; -} -/*-----------------------------------------------------------------*/ -/* inlineFindParm - search an ast tree of parameters to find one */ -/* at a particular index (0=first parameter). */ -/* Returns NULL if not found. */ -/*-----------------------------------------------------------------*/ -static ast *inlineFindParm(ast *parms, int index) { - return inlineFindParmRecurse(parms, &index); + return (index ? 0 : parms); } static void expandInlineFuncs(ast *tree, ast *block); /*-----------------------------------------------------------------*/ -/* expandInlineFuncsInDeclarators - replace calls to inline */ -/* functions with the function itself */ +/* expandInlineFuncsInDeclarators - recursively replace calls to */ +/* inline functions */ /*-----------------------------------------------------------------*/ static void expandInlineFuncsInDeclarators(struct initList *ival, ast *block) { for (; ival; ival = ival->next) { @@ -6059,8 +6805,13 @@ static void expandInlineFuncs(ast *tree, ast *block) { if (csym) func = csym; + if ((inCriticalFunction || inCriticalBlock) && + IFFUNC_ISCRITICAL(func->type)) + werrorfl(tree->left->filename, tree->left->lineno, E_INVALID_CRITICAL); + /* Is this an inline function that we can inline? */ - if (IFFUNC_ISINLINE(func->type) && func->funcTree) { + if (IFFUNC_ISINLINE(func->type) && !IFFUNC_HASVARARGS(func->type) && + func->funcTree) { symbol *retsym = NULL; symbol *retlab; ast *inlinetree; @@ -6071,7 +6822,8 @@ static void expandInlineFuncs(ast *tree, ast *block) { /* Generate a label for the inlined function to branch to */ /* in case it contains a return statement */ - retlab = newSymbol(genSymName(tree->level + 1), tree->level + 1); + retlab = newSymbol(genSymName(tree->level + LEVEL_UNIT), + tree->level + LEVEL_UNIT); retlab->isitmp = 1; retlab->islbl = 1; inlineState.retlab = retlab; @@ -6090,6 +6842,8 @@ static void expandInlineFuncs(ast *tree, ast *block) { temptree = newNode(BLOCK, NULL, temptree); copyAstLoc(temptree, tree); inlinetree2 = temptree; + inlinetree2->level += 2 * LEVEL_UNIT; + inlinetree2->block = blockNo + 2; /* Handle the return type */ if (!IS_VOID(func->type->next)) { @@ -6109,6 +6863,8 @@ static void expandInlineFuncs(ast *tree, ast *block) { inlinetree = newNode(BLOCK, NULL, inlinetree2); copyAstLoc(inlinetree, tree); + inlinetree2->level += LEVEL_UNIT; + inlinetree2->block = blockNo + 1; /* To pass parameters to the inlined function, we need some */ /* intermediate variables. This avoids scoping problems */ @@ -6129,7 +6885,8 @@ static void expandInlineFuncs(ast *tree, ast *block) { /* } */ args = FUNC_ARGS(func->type); argIndex = 0; - while (args) { + + for (; args; args = args->next, argIndex++) { symbol *temparg; ast *assigntree; symbol *parm; @@ -6140,8 +6897,11 @@ static void expandInlineFuncs(ast *tree, ast *block) { break; } - temparg = inlineTempVar(args->sym->type, tree->level + 1); - inlineAddDecl(temparg, inlinetree, FALSE, FALSE); + if (!args->sym) /* skip arg if it's missing a name */ + continue; + + temparg = inlineTempVar(args->sym->type, tree->level + LEVEL_UNIT); + inlineAddDecl(copySymbol(temparg), inlinetree, FALSE, FALSE); assigntree = newNode('=', newAst_VALUE(symbolVal(temparg)), passedarg); assigntree->initMode = 1; // tell that assignment is initializer @@ -6155,11 +6915,13 @@ static void expandInlineFuncs(ast *tree, ast *block) { newAst_VALUE(symbolVal(temparg))); assigntree->initMode = 1; // tell that assignment is initializer inlinetree2->right = newNode(NULLOP, assigntree, inlinetree2->right); - - args = args->next; - argIndex++; + parm->onStack = 0; // stack usage will be recomputed later } + if (inlineFindParm(tree->right, argIndex) && + !IFFUNC_HASVARARGS(func->type)) + werror(E_TOO_MANY_PARMS); + /* Handle the return type */ if (!IS_VOID(func->type->next)) { /* Generate return symbol coma statement; */ @@ -6167,10 +6929,14 @@ static void expandInlineFuncs(ast *tree, ast *block) { /* {{inline_function_code}}, retsym */ tree->opval.op = ','; + if (IFFUNC_ISCRITICAL(func->type)) + inlinetree = newNode(CRITICAL, inlinetree, NULL); tree->left = inlinetree; tree->right = newAst_VALUE(symbolVal(retsym)); } else { tree->opval.op = NULLOP; + if (IFFUNC_ISCRITICAL(func->type)) + inlinetree = newNode(CRITICAL, inlinetree, NULL); tree->left = NULL; tree->right = inlinetree; } @@ -6200,6 +6966,15 @@ static void expandInlineFuncs(ast *tree, ast *block) { } } + if (tree->opval.op == FOR) { + if (AST_FOR(tree, initExpr)) + expandInlineFuncs(AST_FOR(tree, initExpr), block); + if (AST_FOR(tree, condExpr)) + expandInlineFuncs(AST_FOR(tree, condExpr), block); + if (AST_FOR(tree, loopExpr)) + expandInlineFuncs(AST_FOR(tree, loopExpr), block); + } + if (tree->left) expandInlineFuncs(tree->left, block); if (tree->right) @@ -6216,6 +6991,12 @@ symbol *createFunctionDecl(symbol *name) { value *args; sym_link *type; + /* This change would be done by addSymChain() below anyway. + But we need to do it here to avoid checkFunction() to report + a mismatch with an earlier declaration (that already underwent the change). + Fixed bug #2556. */ + changePointer(name->type); + /* if check function return 0 then some problem */ if (checkFunction(name, NULL) == 0) return NULL; @@ -6272,8 +7053,10 @@ ast *createFunction(symbol *name, ast *body) { fprintf(stderr, "SDCCast.c:createFunction(%s)\n", name->name); /* create a dummy block if none exists */ - if (!body) + if (!body) { body = newNode(BLOCK, NULL, NULL); + body->block = ++blockNo; + } noLineno++; @@ -6287,17 +7070,20 @@ ast *createFunction(symbol *name, ast *body) { if (IFFUNC_ISISR(name->type)) stackPtr -= port->stack.direction * port->stack.isr_overhead; - if (IFFUNC_ISREENT(name->type) || options.stackAuto) { + if (options.stackAuto) { if (options.useXstack) xstackPtr -= port->stack.direction * port->stack.reent_overhead; else stackPtr -= port->stack.direction * port->stack.reent_overhead; } + if (IFFUNC_ISBANKEDCALL(name->type)) + stackPtr -= port->stack.direction * port->stack.banked_overhead; + fetype = getSpec(name->type); /* get the specifier for the function */ - /* if this is a reentrant function then */ - if (IFFUNC_ISREENT(name->type)) - reentrant++; + + if (FUNC_ISINLINE(name->type) && FUNC_ISNAKED(name->type)) + werrorfl(name->fileDef, name->lineDef, W_INLINE_NAKED, name->name); inlineState.count = 0; expandInlineFuncs(body, NULL); @@ -6305,7 +7091,8 @@ ast *createFunction(symbol *name, ast *body) { if (FUNC_ISINLINE(name->type)) name->funcTree = copyAst(body); - allocParms(FUNC_ARGS(name->type)); /* allocate the parameters */ + allocParms(FUNC_ARGS(name->type), + IFFUNC_ISSMALLC(name->type)); /* allocate the parameters */ /* do processing for parameters that are passed in registers */ processRegParms(FUNC_ARGS(name->type), body); @@ -6335,6 +7122,7 @@ ast *createFunction(symbol *name, ast *body) { ex = newAst_VALUE(symbolVal(name)); /* create name */ ex = newNode(FUNCTION, ex, body); + ex->block = body->block; ex->values.args = FUNC_ARGS(name->type); ex->decorated = 1; if (options.dump_ast) @@ -6379,9 +7167,6 @@ ast *createFunction(symbol *name, ast *body) { /* deallocate paramaters */ deallocParms(FUNC_ARGS(name->type)); - if (IFFUNC_ISREENT(name->type)) - reentrant--; - /* we are done freeup memory & cleanup */ noLineno--; if (port->reset_labelKey) @@ -6456,10 +7241,12 @@ void ast_print(ast *tree, FILE *outfile, int indent) { if (tree->opval.op == BLOCK) { symbol *decls = tree->values.sym; INDENT(indent, outfile); - fprintf(outfile, "{ L%d B%d\n", tree->level, tree->block); + fprintf(outfile, "{ L%ld:%ld B%d\n", tree->level / LEVEL_UNIT, + tree->level % LEVEL_UNIT, tree->block); while (decls) { INDENT(indent + 2, outfile); - fprintf(outfile, "DECLARE SYMBOL (L%d B%d %s=%p) type (", decls->level, + fprintf(outfile, "DECLARE SYMBOL (L%ld:%ld B%d %s=%p) type (", + decls->level / LEVEL_UNIT, decls->level % LEVEL_UNIT, decls->block, decls->name, decls); printTypeChain(decls->type, outfile); fprintf(outfile, ")"); @@ -6501,13 +7288,22 @@ void ast_print(ast *tree, FILE *outfile, int indent) { if (tree->type == EX_VALUE) { if (IS_LITERAL(tree->opval.val->etype)) { fprintf(outfile, "CONSTANT (%p) value = ", tree); - if (SPEC_USIGN(tree->opval.val->etype)) - fprintf(outfile, "%u", (TYPE_TARGET_ULONG)ulFromVal(tree->opval.val)); - else - fprintf(outfile, "%d", (TYPE_TARGET_LONG)ulFromVal(tree->opval.val)); - fprintf(outfile, ", 0x%x, %f", - (TYPE_TARGET_ULONG)ulFromVal(tree->opval.val), - floatFromVal(tree->opval.val)); + if (SPEC_LONGLONG(tree->opval.val->etype)) { + unsigned long long ull = ullFromVal(tree->opval.val); + + if (SPEC_USIGN(tree->opval.val->etype)) + fprintf(outfile, "%llu, 0x%llx", ull, ull); + else + fprintf(outfile, "%lld, 0x%llx", (signed long long)ull, ull); + } else { + unsigned long ul = ulFromVal(tree->opval.val); + + if (SPEC_USIGN(tree->opval.val->etype)) + fprintf(outfile, "%lu", ul); + else + fprintf(outfile, "%ld", (signed long)ul); + fprintf(outfile, ", 0x%lx, %f", ul, floatFromVal(tree->opval.val)); + } } else if (tree->opval.val->sym) { /* if the undefined flag is set then give error message */ if (tree->opval.val->sym->undefined) { @@ -6515,7 +7311,9 @@ void ast_print(ast *tree, FILE *outfile, int indent) { } else { fprintf(outfile, "SYMBOL "); } - fprintf(outfile, "(L%d B%d %s=%p @ %p)", tree->opval.val->sym->level, + fprintf(outfile, "(L%ld:%ld B%d %s=%p @ %p)", + tree->opval.val->sym->level / LEVEL_UNIT, + tree->opval.val->sym->level % LEVEL_UNIT, tree->opval.val->sym->block, tree->opval.val->sym->name, tree, tree->opval.val->sym); } @@ -7228,10 +8026,10 @@ static ast *offsetofOp_rec(sym_link *type, ast *snd, sym_link **result_type) { checkTypeSanity(type, "(offsetofOp)"); /* offsetof can only be applied to structs/unions */ - if (!IS_STRUCT(type)) { + if (!IS_STRUCT(type) || !getSize(type)) { werrorfl(snd->filename, snd->lineno, E_OFFSETOF_TYPE); - *result_type = NULL; - return NULL; + *result_type = 0; + return newAst_VALUE(valueFromLit(0)); } /* offsetof(struct_type, symbol); */ @@ -7265,5 +8063,6 @@ static ast *offsetofOp_rec(sym_link *type, ast *snd, sym_link **result_type) { ast *offsetofOp(sym_link *type, ast *snd) { sym_link *result_type; + return offsetofOp_rec(type, snd, &result_type); } diff --git a/src/SDCCast.h b/src/SDCCast.h index 05f90c0a8..ca2d880b6 100644 --- a/src/SDCCast.h +++ b/src/SDCCast.h @@ -43,7 +43,7 @@ typedef struct ast { unsigned lvalue : 1; unsigned initMode : 1; unsigned reversed : 1; - int level; /* level for expr */ + long level; /* level for expr */ int block; /* block number */ int seqPoint; /* sequence point */ /* union of values expression can have */ @@ -56,7 +56,7 @@ typedef struct ast { /* union for special processing */ union { - const char *inlineasm; /* pointer to inline assembler code */ + char *inlineasm; /* pointer to inline assembler code */ literalList *constlist; /* init list for array initializer. */ symbol *sym; /* if block then -> symbols */ value *args; /* if function then args */ @@ -192,7 +192,8 @@ ast *forLoopOptForm(ast *); ast *argAst(ast *); ast *resolveSymbols(ast *); void CodePtrPointsToConst(sym_link *t); -void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit); +void checkPtrCast(sym_link *newType, sym_link *orgType, bool implicit, + bool orgIsNullPtrConstant); ast *decorateType(ast *, RESULT_TYPE); ast *createWhile(symbol *, symbol *, symbol *, ast *, ast *); ast *createIf(ast *, ast *, ast *); @@ -206,9 +207,11 @@ int setAstFileLine(ast *, char *, int); symbol *funcOfType(const char *, sym_link *, sym_link *, int, int); symbol *funcOfTypeVarg(const char *, const char *, int, const char **); ast *initAggregates(symbol *, initList *, ast *); +bool astHasVolatile(ast *tree); bool hasSEFcalls(ast *); void addSymToBlock(symbol *, ast *); void freeStringSymbol(symbol *); +value *stringToSymbol(value *val); DEFSETFUNC(resetParmKey); int astErrors(ast *); RESULT_TYPE getResultTypeFromType(sym_link *); diff --git a/src/SDCCbitv.c b/src/SDCCbitv.c index a0a45e98d..51505b237 100644 --- a/src/SDCCbitv.c +++ b/src/SDCCbitv.c @@ -28,22 +28,22 @@ int bitVectDefault = 1024; +#define BYTE_SIZEOF_ELEMENT (sizeof(unsigned int)) +#define BIT_SIZEOF_ELEMENT (BYTE_SIZEOF_ELEMENT * 8) + /* genernal note about a bitvectors: - bit vectors are stored from left to right i.e. - bit position 0 is the MS bit of the first byte - this also means that bit positions must start from 0 */ + bit positions must start from 0 */ /*-----------------------------------------------------------------*/ /* newBitVect - returns a new bitvector of size */ /*-----------------------------------------------------------------*/ bitVect *newBitVect(int size) { bitVect *bvp; - int byteSize; bvp = Safe_calloc(1, sizeof(bitVect)); bvp->size = size; - bvp->bSize = byteSize = (size / 8) + 1; - bvp->vect = Safe_calloc(1, byteSize); + bvp->allocSize = (size + BIT_SIZEOF_ELEMENT - 1) / BIT_SIZEOF_ELEMENT; + bvp->vect = Safe_calloc(BYTE_SIZEOF_ELEMENT, bvp->allocSize); return bvp; } @@ -62,21 +62,24 @@ void freeBitVect(bitVect *bvp) { /* bitVectResize - changes the size of a bit vector */ /*-----------------------------------------------------------------*/ bitVect *bitVectResize(bitVect *bvp, int size) { - int bSize = (size / 8) + 1; + int allocSize; if (!bvp) return newBitVect(size); + allocSize = (size + BIT_SIZEOF_ELEMENT - 1) / BIT_SIZEOF_ELEMENT; + /* if we already have enough space */ - if (bvp->bSize >= bSize) { + if (bvp->allocSize >= allocSize) { if (size > bvp->size) bvp->size = size; return bvp; } - bvp->vect = Clear_realloc(bvp->vect, bvp->bSize, bSize); + bvp->vect = Clear_realloc(bvp->vect, bvp->allocSize * BYTE_SIZEOF_ELEMENT, + allocSize * BYTE_SIZEOF_ELEMENT); bvp->size = size; - bvp->bSize = bSize; + bvp->allocSize = allocSize; return bvp; } @@ -85,8 +88,8 @@ bitVect *bitVectResize(bitVect *bvp, int size) { /* bitVectSetBit - sets a given bit in the bit vector */ /*-----------------------------------------------------------------*/ bitVect *bitVectSetBit(bitVect *bvp, int pos) { - int byteSize; - int offset; + unsigned int index; + unsigned int bitofs; assert(pos >= 0); /* if set is null then allocate it */ @@ -96,9 +99,9 @@ bitVect *bitVectSetBit(bitVect *bvp, int pos) { if (bvp->size <= pos) bvp = bitVectResize(bvp, pos + 2); /* conservatively resize */ - byteSize = pos / 8; - offset = pos % 8; - bvp->vect[byteSize] |= (((unsigned char)1) << (7 - offset)); + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; + bvp->vect[index] |= 1u << bitofs; return bvp; } @@ -106,51 +109,48 @@ bitVect *bitVectSetBit(bitVect *bvp, int pos) { /* bitVectUnSetBit - unsets the value of a bit in a bitvector */ /*-----------------------------------------------------------------*/ void bitVectUnSetBit(const bitVect *bvp, int pos) { - int byteSize; - int offset; + unsigned int index; + unsigned int bitofs; assert(pos >= 0); if (!bvp) return; - byteSize = pos / 8; - if (bvp->bSize <= byteSize) + if (bvp->size <= pos) return; - offset = pos % 8; - - bvp->vect[byteSize] &= ~(((unsigned char)1) << (7 - offset)); + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; + bvp->vect[index] &= ~(1u << bitofs); } /*-----------------------------------------------------------------*/ /* bitVectBitValue - returns value value at bit position */ /*-----------------------------------------------------------------*/ int bitVectBitValue(const bitVect *bvp, int pos) { - int byteSize; - int offset; + unsigned int index; + unsigned int bitofs; assert(pos >= 0); if (!bvp) return 0; - byteSize = pos / 8; + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; - if (bvp->bSize <= byteSize) + if (bvp->size <= pos) return 0; - offset = pos % 8; - - return ((bvp->vect[byteSize] >> (7 - offset)) & ((unsigned char)1)); + return (bvp->vect[index] >> bitofs) & 1; } /*-----------------------------------------------------------------*/ /* bitVectUnion - unions two bitvectors */ /*-----------------------------------------------------------------*/ bitVect *bitVectUnion(bitVect *bvp1, bitVect *bvp2) { - int i; bitVect *newBvp; unsigned int *pn, *p1, *p2; - int nbits; + int elements; /* if both null */ if (!bvp1 && !bvp2) @@ -165,98 +165,150 @@ bitVect *bitVectUnion(bitVect *bvp1, bitVect *bvp2) { /* if they are not the same size */ /* make them the same size */ - if (bvp1->bSize < bvp2->bSize) + if (bvp1->size < bvp2->size) bvp1 = bitVectResize(bvp1, bvp2->size); - else if (bvp2->bSize < bvp1->bSize) + else if (bvp2->size < bvp1->size) bvp2 = bitVectResize(bvp2, bvp1->size); newBvp = newBitVect(bvp1->size); - nbits = bvp1->bSize; - i = 0; + elements = bvp1->allocSize; - pn = (unsigned int *)newBvp->vect; - p1 = (unsigned int *)bvp1->vect; - p2 = (unsigned int *)bvp2->vect; + pn = newBvp->vect; + p1 = bvp1->vect; + p2 = bvp2->vect; - while ((nbits - i) >= sizeof(*pn)) { + while (elements--) { *pn++ = *p1++ | *p2++; - i += sizeof(*pn); } - for (; i < nbits; i++) - newBvp->vect[i] = bvp1->vect[i] | bvp2->vect[i]; - return newBvp; } +/*-----------------------------------------------------------------*/ +/* bitVectInplaceUnion - unions two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect *bitVectInplaceUnion(bitVect *bvp1, bitVect *bvp2) { + unsigned int *p1, *p2; + int elements; + + /* if both null */ + if (!bvp1 && !bvp2) + return NULL; + + /* if one of them null then return the other */ + if (!bvp1 && bvp2) + return bitVectCopy(bvp2); + + if (bvp1 && !bvp2) + return bvp1; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize(bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize(bvp2, bvp1->size); + + elements = bvp1->allocSize; + + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) { + *p1 |= *p2; + p1++; + p2++; + } + + return bvp1; +} + /*-----------------------------------------------------------------*/ /* bitVectIntersect - intersect two bitvectors */ /*-----------------------------------------------------------------*/ bitVect *bitVectIntersect(bitVect *bvp1, bitVect *bvp2) { - int i; bitVect *newBvp; unsigned int *pn, *p1, *p2; - int nbits; + int elements; if (!bvp2 || !bvp1) return NULL; /* if they are not the same size */ /* make them the same size */ - if (bvp1->bSize < bvp2->bSize) - bvp1 = bitVectResize(bvp1, bvp2->bSize); + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize(bvp1, bvp2->size); else if (bvp2->size < bvp1->size) bvp2 = bitVectResize(bvp2, bvp1->size); newBvp = newBitVect(bvp1->size); - nbits = bvp1->bSize; - i = 0; + elements = bvp1->allocSize; - pn = (unsigned int *)newBvp->vect; - p1 = (unsigned int *)bvp1->vect; - p2 = (unsigned int *)bvp2->vect; + pn = newBvp->vect; + p1 = bvp1->vect; + p2 = bvp2->vect; - while ((nbits - i) >= sizeof(*pn)) { + while (elements--) { *pn++ = *p1++ & *p2++; - i += sizeof(*pn); } - for (; i < nbits; i++) - newBvp->vect[i] = bvp1->vect[i] & bvp2->vect[i]; - return newBvp; } +/*-----------------------------------------------------------------*/ +/* bitVectInplaceIntersect - intersect two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect *bitVectInplaceIntersect(bitVect *bvp1, bitVect *bvp2) { + unsigned int *p1, *p2; + int elements; + + if (!bvp2 || !bvp1) + return NULL; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize(bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize(bvp2, bvp1->size); + + elements = bvp1->allocSize; + + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) { + *p1 &= *p2; + p1++; + p2++; + } + + return bvp1; +} + /*-----------------------------------------------------------------*/ /* bitVectBitsInCommon - special case of intersection determines */ /* if the vectors have any common bits set */ /*-----------------------------------------------------------------*/ int bitVectBitsInCommon(const bitVect *bvp1, const bitVect *bvp2) { - int i; - int nbits; + int elements; unsigned int *p1, *p2; if (!bvp1 || !bvp2) return 0; - nbits = min(bvp1->bSize, bvp2->bSize); - i = 0; + elements = min(bvp1->allocSize, bvp2->allocSize); - p1 = (unsigned int *)bvp1->vect; - p2 = (unsigned int *)bvp2->vect; + p1 = bvp1->vect; + p2 = bvp2->vect; - while ((nbits - i) >= sizeof(*p1)) { + while (elements--) { if (*p1 & *p2) return 1; p1++; p2++; - i += sizeof(*p1); } - for (; i < nbits; i++) - if (bvp1->vect[i] & bvp2->vect[i]) - return 1; - return 0; } @@ -264,9 +316,8 @@ int bitVectBitsInCommon(const bitVect *bvp1, const bitVect *bvp2) { /* bitVectCplAnd - complement the second & and it with the first */ /*-----------------------------------------------------------------*/ bitVect *bitVectCplAnd(bitVect *bvp1, bitVect *bvp2) { - int i; unsigned int *p1, *p2; - int nbits; + int elements; if (!bvp2) return bvp1; @@ -276,27 +327,21 @@ bitVect *bitVectCplAnd(bitVect *bvp1, bitVect *bvp2) { /* if they are not the same size */ /* make them the same size */ - if (bvp1->bSize < bvp2->bSize) - bvp1 = bitVectResize(bvp1, bvp2->bSize); + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize(bvp1, bvp2->size); else if (bvp2->size < bvp1->size) bvp2 = bitVectResize(bvp2, bvp1->size); - nbits = bvp1->bSize; - i = 0; - - p1 = (unsigned int *)bvp1->vect; - p2 = (unsigned int *)bvp2->vect; + elements = bvp1->allocSize; + p1 = bvp1->vect; + p2 = bvp2->vect; - while ((nbits - i) >= sizeof(*p1)) { - *p1 = *p1 & (~*p2); + while (elements--) { + *p1 = *p1 & ~*p2; p2++; p1++; - i += sizeof(*p1); } - for (; i < nbits; i++) - bvp1->vect[i] = bvp1->vect[i] & (~bvp2->vect[i]); - return bvp1; } @@ -309,7 +354,7 @@ int bitVectIsZero(const bitVect *bvp) { if (!bvp) return 1; - for (i = 0; i < bvp->bSize; i++) + for (i = 0; i < bvp->allocSize; i++) if (bvp->vect[i] != 0) return 0; @@ -321,6 +366,7 @@ int bitVectIsZero(const bitVect *bvp) { /*-----------------------------------------------------------------*/ int bitVectEqual(bitVect *bvp1, bitVect *bvp2) { int i; + int elements; if (!bvp1 || !bvp2) return 0; @@ -328,13 +374,24 @@ int bitVectEqual(bitVect *bvp1, bitVect *bvp2) { if (bvp1 == bvp2) return 1; - if (bvp1->bSize != bvp2->bSize) - return 0; - - for (i = 0; i < bvp1->bSize; i++) + /* elements common to both allocations must match */ + elements = min(bvp1->allocSize, bvp2->allocSize); + for (i = 0; i < elements; i++) if (bvp1->vect[i] != bvp2->vect[i]) return 0; + /* any extra elements allocated must be 0 */ + if (bvp1->allocSize > elements) { + for (i = elements; i < bvp1->allocSize; i++) + if (bvp1->vect[i]) + return 0; + } + if (bvp2->allocSize > elements) { + for (i = elements; i < bvp2->allocSize; i++) + if (bvp2->vect[i]) + return 0; + } + return 1; } @@ -349,7 +406,7 @@ bitVect *bitVectCopy(const bitVect *bvp) { return NULL; newBvp = newBitVect(bvp->size); - for (i = 0; i < bvp->bSize; i++) + for (i = 0; i < bvp->allocSize; i++) newBvp->vect[i] = bvp->vect[i]; return newBvp; @@ -359,57 +416,24 @@ bitVect *bitVectCopy(const bitVect *bvp) { /* bitVectnBitsOn - returns the number of bits that are on */ /*-----------------------------------------------------------------*/ int bitVectnBitsOn(const bitVect *bvp) { - int i, j; - unsigned char byte; int count = 0; unsigned int *p1; - - /* The bit vector is highest to lowest. Interesting. */ - const unsigned int mask[] = {0, - 128, - 128 + 64, - 128 + 64 + 32, - 128 + 64 + 32 + 16, - 128 + 64 + 32 + 16 + 8, - 128 + 64 + 32 + 16 + 8 + 4, - 128 + 64 + 32 + 16 + 8 + 4 + 2}; + int elements; if (!bvp) return 0; - /* j is the number of bytes in the bitvect */ - j = (bvp->size + 7) / 8; + p1 = bvp->vect; + elements = bvp->allocSize; - /* Fix up the highest bits in the top byte so that we can iterate over - all of them. */ - if (bvp->size % 8 != 0) { - bvp->vect[j - 1] &= mask[bvp->size & 7]; - } - - /* Take care of things in machine word chunks if possible. As we - are only counting bits it does not matter which order they are - counted in. - */ - i = 0; - p1 = (unsigned int *)bvp->vect; - - while ((j - i) >= sizeof(*p1)) { + while (elements--) { unsigned int word = *p1++; while (word) { count++; word &= word - 1; } - i += sizeof(*p1); } - /* Take care of the rest of the bitvect. */ - for (; i < j; i++) { - byte = bvp->vect[i]; - while (byte) { - count++; - byte &= byte - 1; - } - } return count; } @@ -418,16 +442,35 @@ int bitVectnBitsOn(const bitVect *bvp) { /*-----------------------------------------------------------------*/ int bitVectFirstBit(const bitVect *bvp) { int i; + int index; if (!bvp) return -1; - for (i = 0; i < bvp->size; i++) + + for (i = 0, index = 0; i < bvp->size; i += BIT_SIZEOF_ELEMENT, index++) + if (bvp->vect[index]) + break; + + for (; i < bvp->size; i++) if (bitVectBitValue(bvp, i)) return i; return -1; } +/*-----------------------------------------------------------------*/ +/* bitVectClear - clear all bits */ +/*-----------------------------------------------------------------*/ +void bitVectClear(bitVect *bvp) { + int i; + + if (!bvp) + return; + + for (i = 0; i < bvp->allocSize; i++) + bvp->vect[i] = 0; +} + /*-----------------------------------------------------------------*/ /* bitVectDebugOn - prints bits that are on */ /*-----------------------------------------------------------------*/ @@ -439,7 +482,8 @@ void bitVectDebugOn(bitVect *bvp, FILE *of) { if (!bvp) return; - fprintf(of, "bitvector Size = %d bSize = %d\n", bvp->size, bvp->bSize); + fprintf(of, "bitvector Size = %d allocSize = %d\n", bvp->size, + bvp->allocSize); fprintf(of, "Bits on { "); for (i = 0; i < bvp->size; i++) { if (bitVectBitValue(bvp, i)) diff --git a/src/SDCCbitv.h b/src/SDCCbitv.h index 0aed91d71..6b141e165 100644 --- a/src/SDCCbitv.h +++ b/src/SDCCbitv.h @@ -30,9 +30,9 @@ /* bitvector */ typedef struct bitVect { - int size; - int bSize; - unsigned char *vect; + int size; // number of bits + int allocSize; // number of int elements + unsigned int *vect; } bitVect; extern int bitVectDefault; @@ -47,7 +47,9 @@ bitVect *bitVectSetBit(bitVect *, int); void bitVectUnSetBit(const bitVect *, int); int bitVectBitValue(const bitVect *, int); bitVect *bitVectUnion(bitVect *, bitVect *); +bitVect *bitVectInplaceUnion(bitVect *, bitVect *); bitVect *bitVectIntersect(bitVect *, bitVect *); +bitVect *bitVectInplaceIntersect(bitVect *, bitVect *); int bitVectBitsInCommon(const bitVect *, const bitVect *); bitVect *bitVectCplAnd(bitVect *, bitVect *); int bitVectEqual(bitVect *, bitVect *); @@ -55,5 +57,6 @@ bitVect *bitVectCopy(const bitVect *); int bitVectIsZero(const bitVect *); int bitVectnBitsOn(const bitVect *); int bitVectFirstBit(const bitVect *); +void bitVectClear(bitVect *bvp); void bitVectDebugOn(bitVect *, FILE *); #endif diff --git a/src/SDCCbtree.cc b/src/SDCCbtree.cc index 7c75244bd..cb59f6faf 100644 --- a/src/SDCCbtree.cc +++ b/src/SDCCbtree.cc @@ -16,6 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include #include #include @@ -23,10 +24,6 @@ #include "common.h" -#ifdef HAVE_STX_BTREE_MAP_H -#include -#endif - extern "C" { #include "SDCCbtree.h" } @@ -36,13 +33,9 @@ extern "C" { typedef boost::adjacency_list, int>> btree_t; -#ifdef HAVE_STX_BTREE_MAP_H -typedef stx::btree_map bmap_t; -typedef stx::btree_map bmaprev_t; -#else + typedef std::map bmap_t; typedef std::map bmaprev_t; -#endif static btree_t btree; static bmap_t bmap; @@ -163,6 +156,9 @@ void btree_alloc(void) { btree_alloc_subtree(0, 0, 0, &ssize); if (currFunc) { +#ifdef BTREE_DEBUG + std::cout << "btree stack allocation used total of " << ssize << " bytes\n"; +#endif currFunc->stack += ssize; SPEC_STAK(currFunc->etype) += ssize; } diff --git a/src/SDCCcflow.c b/src/SDCCcflow.c index 76aa98ce9..848272d49 100644 --- a/src/SDCCcflow.c +++ b/src/SDCCcflow.c @@ -156,7 +156,16 @@ static void eBBSuccessors(ebbIndex *ebbi) { switch (ic->op) { case GOTO: /* goto has edge to label */ succ = eBBWithEntryLabel(ebbi, ic->label); - break; + + /* Sometimes a block has a GOTO added after the original */ + /* final IFX (due to loop optimizations). If IFX found, */ + /* fall through to handle the IFX too. */ + if (ic->prev && ic->prev->op == IFX) { + if (succ) + addSuccessor(ebbs[i], succ); /* add the GOTO target */ + ic = ic->prev; /* get ready to handle IFX too. */ + } else + break; case IFX: /* conditional jump */ /* if true label is present */ @@ -221,7 +230,7 @@ static void computeDominance(ebbIndex *ebbi) { for (pred = setFirstItem(ebbs[i]->predList), cDomVect = (pred ? bitVectCopy(pred->domVect) : NULL); pred; pred = setNextItem(ebbs[i]->predList)) { - cDomVect = bitVectIntersect(cDomVect, pred->domVect); + cDomVect = bitVectInplaceIntersect(cDomVect, pred->domVect); } if (!cDomVect) cDomVect = newBitVect(count); @@ -229,6 +238,7 @@ static void computeDominance(ebbIndex *ebbi) { cDomVect = bitVectSetBit(cDomVect, ebbs[i]->bbnum); if (!bitVectEqual(cDomVect, ebbs[i]->domVect)) { + freeBitVect(ebbs[i]->domVect); ebbs[i]->domVect = cDomVect; change = 1; } @@ -344,10 +354,12 @@ void computeControlFlow(ebbIndex *ebbi) { /* initialise some things */ for (i = 0; i < ebbi->count; i++) { - setToNull((void *)&ebbs[i]->predList); - setToNull((void *)&ebbs[i]->domVect); - setToNull((void *)&ebbs[i]->succList); - setToNull((void *)&ebbs[i]->succVect); + deleteSet(&ebbs[i]->predList); + freeBitVect(ebbs[i]->domVect); + ebbs[i]->domVect = NULL; + deleteSet(&ebbs[i]->succList); + freeBitVect(ebbs[i]->succVect); + ebbs[i]->succVect = NULL; ebbs[i]->visited = 0; ebbs[i]->dfnum = 0; } diff --git a/src/SDCCcse.c b/src/SDCCcse.c index a06445bc9..6e23d4dcf 100644 --- a/src/SDCCcse.c +++ b/src/SDCCcse.c @@ -39,7 +39,7 @@ cseDef *newCseDef(operand *sym, iCode *ic) { cdp->sym = sym; cdp->diCode = ic; cdp->key = sym->key; - cdp->ancestors = newBitVect(iCodeKey); + cdp->ancestors = newBitVect(operandKey); cdp->fromGlobal = 0; cdp->fromAddrTaken = 0; @@ -67,6 +67,17 @@ cseDef *newCseDef(operand *sym, iCode *ic) { return cdp; } +void freeLocalCseDef(void *item) { + cseDef *cse = (cseDef *)item; + + /* If this CSE definition being deleted is not visible outside */ + /* its defining eBBlock, we can safely deallocate it completely */ + if (!cse->nonLocalCSE) { + freeBitVect(cse->ancestors); + Safe_free(cse); + } +} + void updateCseDefAncestors(cseDef *cdp, set *cseSet) { cseDef *loop; set *sl; @@ -78,7 +89,7 @@ void updateCseDefAncestors(cseDef *cdp, set *cseSet) { for (sl = cseSet; sl; sl = sl->next) { loop = sl->item; if (loop->sym->key == IC_LEFT(ic)->key) { - cdp->ancestors = bitVectUnion(cdp->ancestors, loop->ancestors); + cdp->ancestors = bitVectInplaceUnion(cdp->ancestors, loop->ancestors); cdp->fromGlobal |= loop->fromGlobal; cdp->fromAddrTaken |= loop->fromAddrTaken; break; @@ -90,7 +101,7 @@ void updateCseDefAncestors(cseDef *cdp, set *cseSet) { for (sl = cseSet; sl; sl = sl->next) { loop = sl->item; if (loop->sym->key == IC_RIGHT(ic)->key) { - cdp->ancestors = bitVectUnion(cdp->ancestors, loop->ancestors); + cdp->ancestors = bitVectInplaceUnion(cdp->ancestors, loop->ancestors); cdp->fromGlobal |= loop->fromGlobal; cdp->fromAddrTaken |= loop->fromAddrTaken; break; @@ -276,7 +287,7 @@ DEFSETFUNC(removeFromInExprs) { } /*-----------------------------------------------------------------*/ -/* isGlobalInNearSpace - return TRUE if valriable is a globalin data */ +/* isGlobalInNearSpace - return TRUE if variable is a globalin data */ /*-----------------------------------------------------------------*/ static bool isGlobalInNearSpace(operand *op) { sym_link *type = getSpec(operandType(op)); @@ -541,6 +552,8 @@ DEFSETFUNC(ifDefGlobalAliasableByPtr) { DEFSETFUNC(ifFromAddrTaken) { cseDef *cdp = item; + if (OP_SYMBOL(cdp->sym)->addrtaken) + return 1; return cdp->fromAddrTaken; } @@ -720,7 +733,7 @@ DEFSETFUNC(ifDiCodeIsX) { } /*-----------------------------------------------------------------*/ -/* findBackwardDef - scan backwards to find deinition of operand */ +/* findBackwardDef - scan backwards to find definition of operand */ /*-----------------------------------------------------------------*/ iCode *findBackwardDef(operand *op, iCode *ic) { iCode *lic; @@ -784,8 +797,6 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { } if (IS_ITEMP(IC_LEFT(ic)) && IS_ITEMP(IC_RESULT(ic)) && - /* !OP_SYMBOL(IC_RESULT(ic))->isreqv && */ - /* !OP_SYMBOL(IC_LEFT(ic))->isreqv && */ !IC_LEFT(ic)->isaddr) { ic->op = '='; IC_RIGHT(ic) = operandFromOperand(IC_LEFT(ic)); @@ -801,7 +812,7 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { switch (ic->op) { case '+': /* if adding the same thing change to left shift by 1 */ - if (IC_LEFT(ic)->key == IC_RIGHT(ic)->key && + if (IC_LEFT(ic)->key == IC_RIGHT(ic)->key && !IS_OP_VOLATILE(IC_LEFT(ic)) && !(IS_FLOAT(operandType(IC_RESULT(ic))) || IS_FIXED(operandType(IC_RESULT(ic))))) { ic->op = LEFT_OP; @@ -855,7 +866,7 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { break; case '-': /* if subtracting the same thing then zero */ - if (IC_LEFT(ic)->key == IC_RIGHT(ic)->key) { + if (IC_LEFT(ic)->key == IC_RIGHT(ic)->key && !IS_OP_VOLATILE(IC_LEFT(ic))) { ic->op = '='; IC_RIGHT(ic) = operandFromLit(0); IC_LEFT(ic) = NULL; @@ -1038,7 +1049,8 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { case EQ_OP: case LE_OP: case GE_OP: - if (isOperandEqual(IC_LEFT(ic), IC_RIGHT(ic))) { + if (isOperandEqual(IC_LEFT(ic), IC_RIGHT(ic)) && + !IS_OP_VOLATILE(IC_LEFT(ic))) { ic->op = '='; IC_RIGHT(ic) = operandFromLit(1); IC_LEFT(ic) = NULL; @@ -1048,7 +1060,8 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { case NE_OP: case '>': case '<': - if (isOperandEqual(IC_LEFT(ic), IC_RIGHT(ic))) { + if (isOperandEqual(IC_LEFT(ic), IC_RIGHT(ic)) && + !IS_OP_VOLATILE(IC_LEFT(ic))) { ic->op = '='; IC_RIGHT(ic) = operandFromLit(0); IC_LEFT(ic) = NULL; @@ -1074,8 +1087,8 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { litval = gpVal; } ic->op = '='; - IC_RIGHT(ic) = - operandFromValue(valCastLiteral(operandType(IC_LEFT(ic)), litval)); + IC_RIGHT(ic) = operandFromValue( + valCastLiteral(operandType(IC_LEFT(ic)), litval, litval)); IC_LEFT(ic) = NULL; SET_ISADDR(IC_RESULT(ic), 0); } @@ -1140,8 +1153,12 @@ static void algebraicOpts(iCode *ic, eBBlock *ebp) { } /* if BITWISEAND then check if one of them is 0xff... */ /* if yes turn it into assignment */ + if (IS_BOOLEAN(operandType(IC_RIGHT( + ic)))) /* Special handling since _Bool is stored in 8 bits */ + goto boolcase; switch (bitsForType(operandType(IC_RIGHT(ic)))) { case 1: + boolcase: val = 0x01; break; case 8: @@ -1369,21 +1386,28 @@ void setUsesDefs(operand *op, bitVect *bdefs, bitVect *idefs, bitVect **oud) { /* of these definitions find the ones that are */ /* for this operand */ - adefs = bitVectIntersect(adefs, OP_DEFS(op)); + adefs = bitVectInplaceIntersect(adefs, OP_DEFS(op)); /* these are the definitions that this operand can use */ - op->usesDefs = adefs; + /* Nothing uses op->usesDefs, so why? EEP - 2018-06-10 */ + // op->usesDefs = adefs; /* the out defs is an union */ - *oud = bitVectUnion(*oud, adefs); + *oud = bitVectInplaceUnion(*oud, adefs); + + /* If not assigning op->usesDefs, we can safely free adefs */ + freeBitVect(adefs); } /*-----------------------------------------------------------------*/ /* unsetDefsAndUses - clear this operation for the operands */ /*-----------------------------------------------------------------*/ void unsetDefsAndUses(iCode *ic) { - if (ic->op == JUMPTABLE) + if (ic->op == JUMPTABLE) { + if (IS_SYMOP(IC_JTCOND(ic))) + bitVectUnSetBit(OP_USES(IC_JTCOND(ic)), ic->key); return; + } /* take away this definition from the def chain of the */ /* result & take away from use set of the operands */ @@ -1402,7 +1426,8 @@ void unsetDefsAndUses(iCode *ic) { if (IS_SYMOP(IC_RIGHT(ic))) bitVectUnSetBit(OP_USES(IC_RIGHT(ic)), ic->key); } else - /* must be ifx turn off the use */ if (IS_SYMOP(IC_COND(ic))) + /* must be ifx turn off the use */ + if (IS_SYMOP(IC_COND(ic))) bitVectUnSetBit(OP_USES(IC_COND(ic)), ic->key); } @@ -1421,6 +1446,53 @@ void ifxOptimize(iCode *ic, set *cseSet, int computeOnly, eBBlock *ebb, if (pdop) { ReplaceOpWithCheaperOp(&IC_COND(ic), pdop); (*change)++; + } else if (ic->prev && /* Remove unnecessary casts */ + (ic->prev->op == '=' || ic->prev->op == CAST || + ic->prev->op == '!') && + IS_ITEMP(IC_RESULT(ic->prev)) && + IC_RESULT(ic->prev)->key == IC_COND(ic)->key && + bitVectnBitsOn(OP_USES(IC_RESULT(ic->prev))) <= 1) { + /* Don't do this for "if (--c)", it inhibits DJNZ generation */ + if (!ic->prev->prev || ic->prev->prev->op != '-' || + !IS_OP_LITERAL(IC_RIGHT(ic->prev->prev))) { + sym_link *type = operandType(IC_RESULT(ic->prev)); + if (ic->prev->op != CAST || IS_BOOL(type) || + bitsForType(operandType(IC_RIGHT(ic->prev))) < bitsForType(type)) { + if (!isOperandVolatile(ic->prev->op == '!' ? IC_LEFT(ic->prev) + : IC_RIGHT(ic->prev), + FALSE)) { + if (ic->prev->op == '!') /* Invert jump logic */ + { + symbol *tmp = IC_TRUE(ic); + IC_TRUE(ic) = IC_FALSE(ic); + IC_FALSE(ic) = tmp; + } + bitVectUnSetBit(OP_USES(IC_COND(ic)), ic->key); + ReplaceOpWithCheaperOp(&IC_COND(ic), ic->prev->op == '!' + ? IC_LEFT(ic->prev) + : IC_RIGHT(ic->prev)); + (*change)++; + } +/* There's an optimization opportunity here, but OP_USES doesn't seem to be */ +/* initialized properly at this point. - EEP 2016-08-04 */ +#if 0 + else if (bitVectnBitsOn (OP_USES(IC_COND (ic))) == 1) + { + /* We can replace the iTemp with the original volatile symbol */ + /* but we must make sure the volatile symbol is still accessed */ + /* only once. */ + bitVectUnSetBit (OP_USES (IC_COND (ic)), ic->key); + ReplaceOpWithCheaperOp(&IC_COND (ic), IC_RIGHT (ic->prev)); + (*change)++; + /* Make previous assignment an assignment to self. */ + /* killDeadCode() will eliminiate it. */ + IC_RIGHT (ic->prev) = IC_RESULT (ic->prev); + IC_LEFT (ic->prev) = NULL; + ic->prev->op = '='; + } +#endif + } + } } } @@ -1516,7 +1588,7 @@ int constFold(iCode *ic, set *cseSet) { c = b + 20; */ /* deal with only + & - */ - if (ic->op != '+' && ic->op != '-') + if (ic->op != '+' && ic->op != '-' && ic->op != BITWISEAND) return 0; /* check if operation with a literal */ @@ -1528,6 +1600,22 @@ int constFold(iCode *ic, set *cseSet) { if (!(applyToSet(cseSet, diCodeForSym, IC_LEFT(ic), &dic))) return 0; + if (ic->op == BITWISEAND) /* Optimize out bitwise and of comparion results */ + { + /* check that this results in 0 or 1 only */ + if (dic->op != EQ_OP && dic->op != NE_OP && dic->op != LE_OP && + dic->op != GE_OP && dic->op != '<' && dic->op != '>' && dic->op != '!') + return 0; + + IC_RIGHT(ic) = (operandLitValueUll(IC_RIGHT(ic)) & 1) ? IC_LEFT(ic) + : operandFromLit(0); + + ic->op = '='; + IC_LEFT(ic) = 0; + + return 1; + } + /* check that this is also a +/- */ if (dic->op != '+' && dic->op != '-') return 0; @@ -1657,6 +1745,7 @@ static void deleteGetPointers(set **cseSet, set **pss, operand *op, deleteItemIf(cseSet, ifDefSymIsX, cop); deleteItemIf(pss, ifPointerSet, cop); } + deleteSet(&compItems); } /*-----------------------------------------------------------------*/ @@ -1685,6 +1774,12 @@ DEFSETFUNC(delGetPointerSucc) { static void fixUpTypes(iCode *ic) { sym_link *t1 = operandType(IC_LEFT(ic)), *t2; + /* if (TARGET_IS_DS390) */ + if (options.model == MODEL_FLAT24) { + /* hack-o-matic! */ + return; + } + /* for pointer_gets if the types of result & left r the same then change it type of result to next */ if (IS_PTR(t1) && compareType(t2 = operandType(IC_RESULT(ic)), t1) == 1) { @@ -1770,27 +1865,43 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { eBBlock **ebbs = ebbi->bbOrder; int count = ebbi->count; set *cseSet; + set *setnode; iCode *ic; int change = 0; int i; set *ptrSetSet = NULL; cseDef *expr; + int replaced; + int recomputeDataFlow = 0; /* if this block is not reachable */ - if (ebb->noPath) + if (ebb->noPath) { + for (ic = ebb->sch; ic; ic = ic->next) + if (!SKIP_IC2(ic)) + unsetDefsAndUses(ic); return 0; + } + /* Mark incoming subexpressions as non-local */ + for (setnode = ebb->inExprs; setnode; setnode = setnode->next) { + expr = (cseDef *)setnode->item; + expr->nonLocalCSE = 1; + } /* set of common subexpressions */ cseSet = setFromSet(ebb->inExprs); /* these will be computed by this routine */ - setToNull((void *)&ebb->outDefs); - setToNull((void *)&ebb->defSet); - setToNull((void *)&ebb->usesDefs); - setToNull((void *)&ebb->ptrsSet); - setToNull((void *)&ebb->addrOf); - setToNull((void *)&ebb->ldefs); - + freeBitVect(ebb->outDefs); + ebb->outDefs = NULL; + freeBitVect(ebb->defSet); + ebb->defSet = NULL; + freeBitVect(ebb->usesDefs); + ebb->usesDefs = NULL; + freeBitVect(ebb->ptrsSet); + ebb->ptrsSet = NULL; + deleteSet(&ebb->addrOf); + freeBitVect(ebb->ldefs); + ebb->ldefs = NULL; ebb->outDefs = bitVectCopy(ebb->inDefs); bitVectDefault = iCodeKey; ebb->defSet = newBitVect(iCodeKey); @@ -1812,7 +1923,7 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { to a temp then do pointer post inc/dec optimization */ if (ic->op == '=' && !POINTER_SET(ic) && IS_PTR(operandType(IC_RESULT(ic)))) { - ptrPostIncDecOpt(ic); + ptrPostIncDecOpt(ic, ebb); } /* clear the def & use chains for the operands involved */ @@ -1829,19 +1940,19 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { setUsesDefs(IC_RESULT(ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); /* delete global variables from the cseSet since they can be modified by the function call */ - deleteItemIf(&cseSet, ifDefGlobal); + destructItemIf(&cseSet, freeLocalCseDef, ifDefGlobal); /* and also iTemps derived from globals */ - deleteItemIf(&cseSet, ifFromGlobal); + destructItemIf(&cseSet, freeLocalCseDef, ifFromGlobal); /* Delete iTemps derived from symbols whose address */ /* has been taken */ - deleteItemIf(&cseSet, ifFromAddrTaken); + destructItemIf(&cseSet, freeLocalCseDef, ifFromAddrTaken); /* delete all getpointer iCodes from cseSet, this should be done only for global arrays & pointers but at this point we don't know if globals, so to be safe do all */ - deleteItemIf(&cseSet, ifAnyGetPointer); + destructItemIf(&cseSet, freeLocalCseDef, ifAnyGetPointer); /* can't cache pointer set/get operations across a call */ deleteSet(&ptrSetSet); @@ -1952,6 +2063,7 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { } checkSign = isSignedOp(ic); + replaced = 0; /* do the operand lookup i.e. for both the */ /* right & left operand : check the cseSet */ @@ -1971,7 +2083,7 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { if (bitVectBitValue(ebb->ndompset, IC_LEFT(ic)->key)) ebb->ptrsSet = bitVectSetBit(ebb->ptrsSet, pdop->key); ReplaceOpWithCheaperOp(&IC_LEFT(ic), pdop); - change = 1; + change = replaced = 1; } /* check if there is a pointer set for the same pointer visible if yes @@ -1984,10 +2096,11 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { IC_LEFT(ic) = NULL; ReplaceOpWithCheaperOp(&IC_RIGHT(ic), pdop); SET_ISADDR(IC_RESULT(ic), 0); + replaced = 1; } } else { ReplaceOpWithCheaperOp(&IC_LEFT(ic), pdop); - change = 1; + change = replaced = 1; } } } @@ -1998,7 +2111,7 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { applyToSetFTrue(cseSet, findCheaperOp, IC_RIGHT(ic), &pdop, checkSign); if (pdop) { ReplaceOpWithCheaperOp(&IC_RIGHT(ic), pdop); - change = 1; + change = replaced = 1; } } @@ -2024,6 +2137,9 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { /* if after all this it becomes an assignment to self then delete it and continue */ if (ASSIGNMENT_TO_SELF(ic) && !isOperandVolatile(IC_RIGHT(ic), FALSE)) { + bitVectUnSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); + if (IS_SYMOP(IC_RIGHT(ic))) + bitVectUnSetBit(OP_USES(IC_RIGHT(ic)), ic->key); remiCodeFromeBBlock(ebb, ic); continue; } @@ -2069,7 +2185,7 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { if (!(POINTER_SET(ic)) && IC_RESULT(ic)) { cseDef *csed; - deleteItemIf(&cseSet, ifDefSymIsX, IC_RESULT(ic)); + destructItemIf(&cseSet, freeLocalCseDef, ifDefSymIsX, IC_RESULT(ic)); csed = newCseDef(IC_RESULT(ic), ic); updateCseDefAncestors(csed, cseSet); addSetHead(&cseSet, csed); @@ -2106,16 +2222,18 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { /* operands matching the result of this */ /* except in case of pointer access */ if (!(POINTER_SET(ic)) && IS_SYMOP(IC_RESULT(ic))) { - deleteItemIf(&cseSet, ifOperandsHave, IC_RESULT(ic)); + destructItemIf(&cseSet, freeLocalCseDef, ifOperandsHave, IC_RESULT(ic)); deleteItemIf(&ptrSetSet, ifOperandsHave, IC_RESULT(ic)); /* delete any previous definitions */ ebb->defSet = bitVectCplAnd(ebb->defSet, OP_DEFS(IC_RESULT(ic))); /* Until pointer tracking is complete, by conservative and delete all */ /* pointer accesses that might alias this symbol. */ - if (isOperandGlobal(IC_RESULT(ic))) { + if (isOperandGlobal(IC_RESULT(ic)) || + OP_SYMBOL(IC_RESULT(ic))->addrtaken) { memmap *map = SPEC_OCLS(getSpec(operandType(IC_RESULT(ic)))); - deleteItemIf(&cseSet, ifAnyUnrestrictedGetPointer, map->ptrType); + destructItemIf(&cseSet, freeLocalCseDef, ifAnyUnrestrictedGetPointer, + map->ptrType); deleteItemIf(&ptrSetSet, ifAnyUnrestrictedSetPointer, map->ptrType); } } @@ -2159,14 +2277,16 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { /* variable used by this function, so delete them */ /* and any derived symbols from cseSet. */ if (!IS_PTR_RESTRICT(ptype)) { - deleteItemIf(&cseSet, ifDefGlobalAliasableByPtr); - deleteItemIf(&cseSet, ifFromGlobalAliasableByPtr, DCL_TYPE(ptype)); + destructItemIf(&cseSet, freeLocalCseDef, ifDefGlobalAliasableByPtr, + DCL_TYPE(ptype)); + destructItemIf(&cseSet, freeLocalCseDef, ifFromGlobalAliasableByPtr, + DCL_TYPE(ptype)); } /* This could be made more specific for better optimization, but */ /* for safety, delete anything this write may have modified. */ - deleteItemIf(&cseSet, ifFromAddrTaken); - deleteItemIf(&cseSet, ifAnyGetPointer); + destructItemIf(&cseSet, freeLocalCseDef, ifFromAddrTaken); + destructItemIf(&cseSet, freeLocalCseDef, ifAnyGetPointer); } else { /* add the result to definition set */ if (IS_SYMOP(IC_RESULT(ic))) { @@ -2182,8 +2302,15 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { /* delete it from the cseSet */ if (defic->op == ADDRESS_OF) { addSetHead(&ebb->addrOf, IC_LEFT(ic)); - deleteItemIf(&cseSet, ifDefSymIsX, IC_LEFT(ic)); + destructItemIf(&cseSet, freeLocalCseDef, ifDefSymIsX, IC_LEFT(ic)); } + + /* If this was previously in the out expressions in the */ + /* original form, it might need to be killed by another block */ + /* in the new form if we have replaced operands, so recompute */ + /* the data flow after we finish this block */ + if (replaced && ifDiCodeIs(ebb->outExprs, ic)) + recomputeDataFlow = 1; } for (expr = setFirstItem(ebb->inExprs); expr; @@ -2192,10 +2319,21 @@ int cseBBlock(eBBlock *ebb, int computeOnly, ebbIndex *ebbi) { !isinSetWith(ebb->killedExprs, expr, isCseDefEqual)) { addSetHead(&ebb->killedExprs, expr); } - setToNull((void *)&ebb->outExprs); + + deleteSet(&ptrSetSet); + deleteSet(&ebb->outExprs); ebb->outExprs = cseSet; - ebb->outDefs = bitVectUnion(ebb->outDefs, ebb->defSet); - ebb->ptrsSet = bitVectUnion(ebb->ptrsSet, ebb->inPtrsSet); + ebb->outDefs = bitVectInplaceUnion(ebb->outDefs, ebb->defSet); + ebb->ptrsSet = bitVectInplaceUnion(ebb->ptrsSet, ebb->inPtrsSet); + + for (setnode = ebb->outExprs; setnode; setnode = setnode->next) { + expr = (cseDef *)setnode->item; + expr->nonLocalCSE = 1; + } + + if (recomputeDataFlow) + computeDataFlow(ebbi); + return change; } @@ -2215,3 +2353,37 @@ int cseAllBlocks(ebbIndex *ebbi, int computeOnly) { return change; } + +/*------------------------------------------------------------------*/ +/* freeCSEdata - free data created by cseBBlock */ +/*------------------------------------------------------------------*/ +void freeCSEdata(eBBlock *ebb) { + set *s; + + /* We should really free the cseDefs too, but I haven't */ + /* found a good way to do this yet. For the moment, at */ + /* least free up the associated bitVects - EEP */ + for (s = ebb->outExprs; s; s = s->next) { + cseDef *cdp = s->item; + if (!cdp) + continue; + if (cdp->ancestors) { + freeBitVect(cdp->ancestors); + cdp->ancestors = NULL; + } + } + + deleteSet(&ebb->inExprs); + deleteSet(&ebb->outExprs); + deleteSet(&ebb->killedExprs); + + freeBitVect(ebb->inDefs); + freeBitVect(ebb->outDefs); + freeBitVect(ebb->defSet); + freeBitVect(ebb->ldefs); + freeBitVect(ebb->usesDefs); + freeBitVect(ebb->ptrsSet); + freeBitVect(ebb->inPtrsSet); + freeBitVect(ebb->ndompset); + deleteSet(&ebb->addrOf); +} diff --git a/src/SDCCcse.h b/src/SDCCcse.h index d21fe372c..28b05fa7a 100644 --- a/src/SDCCcse.h +++ b/src/SDCCcse.h @@ -31,12 +31,14 @@ typedef struct cseDef { int key; - operand *sym; /* defining symbol */ - iCode *diCode; /* defining instruction */ - bitVect *ancestors; /* keys of the symbol's ancestors */ - int fromGlobal; /* defining symbol's value computed from a global */ - int fromAddrTaken : 1; /* defining symbol's value computed from a */ - /* symbol whose address was taken */ + operand *sym; /* defining symbol */ + iCode *diCode; /* defining instruction */ + bitVect *ancestors; /* keys of the symbol's ancestors */ + int fromGlobal; /* defining symbol's value computed from a global */ + int fromAddrTaken : 1; /* defining symbol's value computed from a */ + /* symbol whose address was taken */ + unsigned nonLocalCSE : 1; /* CSE def visible outside of originating */ + /* basic block */ } cseDef; cseDef *newCseDef(operand *, iCode *); @@ -57,4 +59,6 @@ void setUsesDefs(operand *, bitVect *, bitVect *, bitVect **); void replaceAllSymBySym(iCode *, operand *, operand *, bitVect **); iCode *findBackwardDef(operand *, iCode *); void ReplaceOpWithCheaperOp(operand **op, operand *cop); +void freeCSEdata(eBBlock *); + #endif diff --git a/src/SDCCdflow.c b/src/SDCCdflow.c index 82ed16d0c..2467ce985 100644 --- a/src/SDCCdflow.c +++ b/src/SDCCdflow.c @@ -62,11 +62,11 @@ DEFSETFUNC(ifKilledInBlock) { /* this one off then check if there are other definitions */ bitVectUnSetBit(outs = bitVectCopy(src->outDefs), cdp->diCode->key); if (bitVectBitsInCommon(outs, OP_DEFS(cdp->sym))) { - setToNull((void *)&outs); + freeBitVect(outs); return 1; } - setToNull((void *)&outs); + freeBitVect(outs); /* if the operands of this one was changed in the block */ /* then delete it */ @@ -105,8 +105,8 @@ DEFSETFUNC(mergeInExprs) { dest->ndompset = bitVectCopy(ebp->ndompset); } else { dest->inExprs = intersectSets(dest->inExprs, ebp->outExprs, THROW_DEST); - dest->inPtrsSet = bitVectUnion(dest->inPtrsSet, ebp->ptrsSet); - dest->ndompset = bitVectUnion(dest->ndompset, ebp->ndompset); + dest->inPtrsSet = bitVectInplaceUnion(dest->inPtrsSet, ebp->ptrsSet); + dest->ndompset = bitVectInplaceUnion(dest->ndompset, ebp->ndompset); } } else { // if (dest != ebp) @@ -116,7 +116,7 @@ DEFSETFUNC(mergeInExprs) { /* delete only if killed in this block*/ deleteItemIf(&dest->inExprs, ifKilledInBlock, ebp); /* union the ndompset with pointers set in this block */ - dest->ndompset = bitVectUnion(dest->ndompset, ebp->ptrsSet); + dest->ndompset = bitVectInplaceUnion(dest->ndompset, ebp->ptrsSet); } *firstTime = 0; @@ -136,7 +136,7 @@ DEFSETFUNC(mergeInDefs) { if (!dest->inDefs && *firstTime) dest->inDefs = bitVectCopy(ebp->outDefs); else - dest->inDefs = bitVectUnion(dest->inDefs, ebp->outDefs); + dest->inDefs = bitVectInplaceUnion(dest->inDefs, ebp->outDefs); *firstTime = 0; @@ -153,7 +153,7 @@ void computeDataFlow(ebbIndex *ebbi) { int change; for (i = 0; i < count; i++) - ebbs[i]->killedExprs = NULL; + deleteSet(&ebbs[i]->killedExprs); do { change = 0; @@ -182,7 +182,8 @@ void computeDataFlow(ebbIndex *ebbi) { oldKilledExprs = setFromSet(ebbs[i]->killedExprs); } oldOutDefs = bitVectCopy(ebbs[i]->outDefs); - setToNull((void *)&ebbs[i]->inDefs); + freeBitVect(ebbs[i]->inDefs); + ebbs[i]->inDefs = NULL; /* indefitions are easy just merge them by union */ /* these are the definitions that can possibly */ @@ -207,7 +208,8 @@ void computeDataFlow(ebbIndex *ebbi) { /* figure out the incoming expressions */ /* this is a little more complex */ - setToNull((void *)&ebbs[i]->inExprs); + // setToNull ((void *) &ebbs[i]->inExprs); + deleteSet(&ebbs[i]->inExprs); if (optimize.global_cse) { firstTime = 1; applyToSet(pred, mergeInExprs, ebbs[i], &firstTime); @@ -226,6 +228,9 @@ void computeDataFlow(ebbIndex *ebbi) { isCseDefEqual); } change += !bitVectEqual(ebbs[i]->outDefs, oldOutDefs); + freeBitVect(oldOutDefs); + deleteSet(&oldOutExprs); + deleteSet(&oldKilledExprs); } } while (change); /* iterate till no change */ diff --git a/src/SDCCerr.c b/src/SDCCerr.c index 09e8a4b05..2f0f59aae 100644 --- a/src/SDCCerr.c +++ b/src/SDCCerr.c @@ -18,6 +18,14 @@ #include #include +#include + +#include "SDCCglobl.h" +#include "dbuf_string.h" +#ifdef HAVE_BACKTRACE_SYMBOLS_FD +#include +#include +#endif #include "SDCCerr.h" @@ -51,8 +59,8 @@ struct { 0}, {E_SYNTAX_ERROR, ERROR_LEVEL_ERROR, "Syntax error, declaration ignored at '%s'", 0}, - {E_CONST_EXPECTED, ERROR_LEVEL_ERROR, "Initializer element is not constant", - 0}, + {E_CONST_EXPECTED, ERROR_LEVEL_ERROR, + "Initializer element is not a constant expression", 0}, {E_OUT_OF_MEM, ERROR_LEVEL_ERROR, "'malloc' failed file '%s' for size %ld", 0}, {E_FILE_OPEN_ERR, ERROR_LEVEL_ERROR, "'fopen' failed on file '%s'", 0}, @@ -107,10 +115,9 @@ struct { {E_UNARY_OP, ERROR_LEVEL_ERROR, "'unary %c': illegal operand", 0}, {E_CONV_ERR, ERROR_LEVEL_ERROR, "conversion error: integral promotion failed", 0}, - {E_INT_REQD, ERROR_LEVEL_ERROR, "type must be INT for bit field definition", - 0}, + {E_BITFLD_TYPE, ERROR_LEVEL_ERROR, "invalid type for bit-field", 0}, {E_BITFLD_SIZE, ERROR_LEVEL_ERROR, - "bit field size cannot be greater than int (%d bits)", 0}, + "bit-field size too wide for type (max %d bits)", 0}, {W_TRUNCATION, ERROR_LEVEL_WARNING, "high order truncation might occur", 0}, {E_CODE_WRITE, ERROR_LEVEL_ERROR, "Attempt to assign value to a constant variable (%s)", 0}, @@ -129,14 +136,13 @@ struct { {E_PTR_PLUS_PTR, ERROR_LEVEL_ERROR, "pointer + pointer invalid", 0}, {E_SHIFT_OP_INVALID, ERROR_LEVEL_ERROR, "invalid operand for shift operator", 0}, - {E_COMPARE_OP, ERROR_LEVEL_ERROR, "compare operand cannot be struct/union", - 0}, + {E_COMPARE_OP, ERROR_LEVEL_ERROR, "operands are not comparable", 0}, {E_BITWISE_OP, ERROR_LEVEL_ERROR, "operand invalid for bitwise operation", 0}, {E_ANDOR_OP, ERROR_LEVEL_ERROR, "Invalid operand for '&&' or '||'", 0}, {E_TYPE_MISMATCH, ERROR_LEVEL_ERROR, "indirections to different types %s %s ", 0}, - {E_AGGR_ASSIGN, ERROR_LEVEL_ERROR, "cannot assign values to aggregates", 0}, + {E_ARRAY_ASSIGN, ERROR_LEVEL_ERROR, "cannot assign values to arrays", 0}, {E_ARRAY_DIRECT, ERROR_LEVEL_ERROR, "bit Arrays can be accessed by literal index only", 0}, {E_BIT_ARRAY, ERROR_LEVEL_ERROR, @@ -221,9 +227,10 @@ struct { {W_PRE_PROC_WARNING, ERROR_LEVEL_WARNING, "Pre-Processor %s", 0}, {E_STRUCT_AS_ARG, ERROR_LEVEL_ERROR, "SDCC cannot pass structure '%s' as function argument", 0}, - {E_PREV_DEF_CONFLICT, ERROR_LEVEL_ERROR, - "conflict with previous definition of '%s' for attribute '%s'", 0}, - {E_CODE_NO_INIT, ERROR_LEVEL_ERROR, + {E_PREV_DECL_CONFLICT, ERROR_LEVEL_ERROR, + "conflict with previous declaration of '%s' for attribute '%s' at %s:%d", + 0}, + {E_CODE_NO_INIT, ERROR_LEVEL_WARNING, "variable '%s' declared in code space must have initialiser", 0}, {E_OPS_INTEGRAL, ERROR_LEVEL_ERROR, "operands not integral for assignment operation", 0}, @@ -259,13 +266,13 @@ struct { "unknown compiler option '%s' ignored", 0}, {W_UNSUPP_OPTION, ERROR_LEVEL_WARNING, "option '%s' no longer supported '%s' ", 0}, - {W_UNKNOWN_FEXT, ERROR_LEVEL_WARNING, + {E_UNKNOWN_FEXT, ERROR_LEVEL_ERROR, "don't know what to do with file '%s'. file extension unsupported", 0}, {W_TOO_MANY_SRC, ERROR_LEVEL_WARNING, "cannot compile more than one source file. file '%s' ignored", 0}, {I_CYCLOMATIC, ERROR_LEVEL_INFO, "function '%s', # edges %d , # nodes %d , cyclomatic complexity %d", 0}, - {E_DIVIDE_BY_ZERO, ERROR_LEVEL_ERROR, "dividing by ZERO", 0}, + {E_DIVIDE_BY_ZERO, ERROR_LEVEL_WARNING, "dividing by 0", 0}, {E_FUNC_BIT, ERROR_LEVEL_ERROR, "function cannot return 'bit'", 0}, {E_CAST_ZERO, ERROR_LEVEL_ERROR, "casting from to type 'void' is illegal", 0}, @@ -358,8 +365,8 @@ struct { "function attribute following non-function declaration"}, {W_SAVE_RESTORE, ERROR_LEVEL_PEDANTIC, "unmatched #pragma save and #pragma restore", 0}, - {E_INVALID_CRITICAL, ERROR_LEVEL_ERROR, "not allowed in a critical section", - 0}, + {E_INVALID_CRITICAL, ERROR_LEVEL_ERROR, + "not allowed in a critical section or critical function", 0}, {E_NOT_ALLOWED, ERROR_LEVEL_ERROR, "%s not allowed here", 0}, {E_BAD_TAG, ERROR_LEVEL_ERROR, "'%s' is not a %s tag", 0}, {E_ENUM_NON_INTEGER, ERROR_LEVEL_ERROR, @@ -372,7 +379,8 @@ struct { {W_SIZEOF_VOID, ERROR_LEVEL_WARNING, "size of void is zero", 0}, {W_POSSBUG2, ERROR_LEVEL_WARNING, "possible code generation error at %s line %d,\n" - " please report problem and send source code at SDCC-USER list on SF.Net"}, + " please report problem and send source code at sdcc-user list on " + "sourceforge.net"}, {W_COMPLEMENT, ERROR_LEVEL_WARNING, "using ~ on bit/bool/unsigned char variables can give unexpected results " "due to promotion to int", @@ -394,15 +402,15 @@ struct { {W_C89_NO_FLEXARRAY, ERROR_LEVEL_PEDANTIC, "ISO C90 does not support flexible array members", 0}, {E_FLEXARRAY_NOTATEND, ERROR_LEVEL_ERROR, - "flexible array member not at end of struct", 0}, + "flexible array member '%s' not at end of struct", 0}, {E_FLEXARRAY_INEMPTYSTRCT, ERROR_LEVEL_ERROR, - "flexible array in otherwise empty struct", 0}, + "flexible array '%s' in otherwise empty struct", 0}, {W_EMPTY_SOURCE_FILE, ERROR_LEVEL_PEDANTIC, "ISO C forbids an empty source file", 0}, {W_BAD_PRAGMA_ARGUMENTS, ERROR_LEVEL_WARNING, "#pragma %s: bad argument(s); pragma ignored", 0}, {E_BAD_RESTRICT, ERROR_LEVEL_ERROR, - "Only pointers may be qualified with 'restrict'", 0}, + "Only object pointers may be qualified with 'restrict'", 0}, {E_BAD_INLINE, ERROR_LEVEL_ERROR, "Only functions may be qualified with 'inline'", 0}, {E_BAD_INT_ARGUMENT, ERROR_LEVEL_ERROR, @@ -414,7 +422,7 @@ struct { {W_DEPRECATED_KEYWORD, ERROR_LEVEL_WARNING, "keyword '%s' is deprecated, use '%s' instead", 0}, {E_STORAGE_CLASS_FOR_PARAMETER, ERROR_LEVEL_ERROR, - "storage class specified for parameter '%s'", 0}, + "storage class other than register specified for parameter '%s'", 0}, {E_OFFSETOF_TYPE, ERROR_LEVEL_ERROR, "offsetof can only be applied to structs/unions", 0}, {E_INCOMPLETE_FIELD, ERROR_LEVEL_ERROR, "field '%s' has incomplete type", @@ -426,9 +434,9 @@ struct { {W_DUPLICATE_INIT, ERROR_LEVEL_WARNING, "Duplicate initializer at position %d; ignoring previous.", 0}, {E_INVALID_UNIVERSAL, ERROR_LEVEL_ERROR, - "incomplete universal character name \\%s.", 0}, - {W_UNIVERSAL_C99, ERROR_LEVEL_WARNING, - "universal character names are only valid in C99", 0}, + "invalid universal character name \\%s.", 0}, + {W_UNIVERSAL_C95, ERROR_LEVEL_WARNING, + "universal character names are only valid in C95 or later", 0}, {E_SHORTLONG, ERROR_LEVEL_ERROR, "invalid combination of short / long", 0}, {E_INTEGERSUFFIX, ERROR_LEVEL_ERROR, "Invalid integer suffix '%s' in integer constant", 0}, @@ -439,7 +447,7 @@ struct { {W_STRING_CANNOT_BE_TERMINATED, ERROR_LEVEL_PEDANTIC, "string '%s'cannot be terminated within array", 0}, {W_LONGLONG_LITERAL, ERROR_LEVEL_WARNING, - "support for long long literals is incomplete", 0}, + "support for large long long literals is incomplete", 1}, {S_SYNTAX_ERROR, ERROR_LEVEL_SYNTAX_ERROR, "token -> '%s' ; column %d", 0}, {E_MIXING_CONFIG, ERROR_LEVEL_ERROR, "mixing __CONFIG and CONFIG directives", 0}, @@ -451,6 +459,83 @@ struct { 0}, {W_UNRECOGNIZED_ASM, ERROR_LEVEL_INFO, "%s() failed to parse line node, assuming %d bytes\n'%s'\n", 0}, + {W_FLEXARRAY_INSTRUCT, ERROR_LEVEL_WARNING, + "type of variable '%s' is struct with flexible array field", 0}, + {E_TYPE_IS_FUNCTION, ERROR_LEVEL_ERROR, "'%s' has function type", 0}, + {W_INLINE_NAKED, ERROR_LEVEL_WARNING, "inline function '%s' is __naked", 0}, + {E_Z88DK_FASTCALL_PARAMETERS, ERROR_LEVEL_ERROR, + "invalid number of parameters for __z88dk_fastcall", 0}, + {E_Z88DK_FASTCALL_PARAMETER, ERROR_LEVEL_ERROR, + "ivalid parameter type in __z88dk_fastcall", 0}, + {W_REPEAT_QUALIFIER, ERROR_LEVEL_WARNING, "duplicate specifier '%s'", 0}, + {W_NO_TYPE_SPECIFIER, ERROR_LEVEL_WARNING, "no type specifier for '%s'", 0}, + {E_NO_TYPE_SPECIFIER, ERROR_LEVEL_ERROR, "no type specifier for '%s'", 0}, + {E_MULTIPLE_DEFAULT_IN_GENERIC, ERROR_LEVEL_ERROR, + "multiple default expressions in generic association", 0}, + {E_MULTIPLE_MATCHES_IN_GENERIC, ERROR_LEVEL_ERROR, + "multiple matching expressions in generic association", 0}, + {E_NO_MATCH_IN_GENERIC, ERROR_LEVEL_ERROR, + "no matching expression in generic association", 0}, + {W_LABEL_WITHOUT_STATEMENT, ERROR_LEVEL_WARNING, "label without statement", + 0}, + {E_WCHAR_CONST_C95, ERROR_LEVEL_ERROR, + "character constant of type wchar_t requires C95 or later", 0}, + {E_WCHAR_CONST_C11, ERROR_LEVEL_ERROR, + "character constant of type char16_t or char32_t requires C11 or later", + 0}, + {E_WCHAR_STRING_C95, ERROR_LEVEL_ERROR, + "wide character string of type L requires C95 or later", 0}, + {E_WCHAR_STRING_C11, ERROR_LEVEL_ERROR, + "wide character string of type u8, u, U requires C11 or later", 0}, + {W_UNKNOWN_REG, ERROR_LEVEL_WARNING, "unknown register specification %s", + 0}, + {E_HEXFLOAT_C99, ERROR_LEVEL_ERROR, + "hexadecimal floating constant requires ISO C99 or later", 0}, + {E_ANONYMOUS_STRUCT_TAG, ERROR_LEVEL_ERROR, + "tagged anonymous struct/union '%s'", 0}, + {W_INLINE_FUNCATTR, ERROR_LEVEL_WARNING, + "inline function '%s' might lose function attributes", 0}, + {E_FOR_INITAL_DECLARATION_C99, ERROR_LEVEL_ERROR, + "initial declaration in for loop requires ISO C99 or later", 0}, + {E_QUALIFIED_ARRAY_PARAM_C99, ERROR_LEVEL_ERROR, + "qualifiers in array parameters require ISO C99 or later", 0}, + {E_QUALIFIED_ARRAY_NOPARAM, ERROR_LEVEL_ERROR, + "qualifier or static in array declarator that is not a parameter", 0}, + {E_STATIC_ARRAY_PARAM_C99, ERROR_LEVEL_ERROR, + "static in array parameters requires ISO C99 or later", 0}, + {E_INT_MULTIPLE, ERROR_LEVEL_ERROR, "multiple interrupt numbers for '%s'", + 0}, + {W_INCOMPAT_PTYPES, ERROR_LEVEL_WARNING, "pointer types incompatible ", 0}, + {E_STATIC_ASSERTION_C2X, ERROR_LEVEL_ERROR, + "static assertion with one argument requires C2X or later", 0}, + {W_STATIC_ASSERTION_2, ERROR_LEVEL_WARNING, "static assertion failed", 0}, + {E_DECL_AFTER_STATEMENT_C99, ERROR_LEVEL_ERROR, + "declaration after statement requires ISO C99 or later", 0}, + {E_SHORTCALL_INVALID_VALUE, ERROR_LEVEL_ERROR, + "invalid value for __z88dk_shortcall %s parameter: %x", 0}, + {E_DUPLICATE_PARAMTER_NAME, ERROR_LEVEL_ERROR, + "duplicate parameter name %s for function %s", 0}, + {E_AUTO_FILE_SCOPE, ERROR_LEVEL_ERROR, "auto in declaration at file scope", + 0}, + {E_U8_CHAR_C2X, ERROR_LEVEL_ERROR, + "u8 character constant requires ISO C2X or later", 0}, + {E_U8_CHAR_INVALID, ERROR_LEVEL_ERROR, "invalid u8 character constant", 0}, + {E_ATTRIBUTE_C2X, ERROR_LEVEL_ERROR, "attribute requires C2X or later", 0}, + {E_COMPOUND_LITERALS_C99, ERROR_LEVEL_ERROR, + "compound literals require ISO C99 or later and are not implemented", 0}, + {E_THREAD_LOCAL, ERROR_LEVEL_ERROR, + "thread-local storage is not implemented", 0}, + {E_ENUM_COMMA_C99, ERROR_LEVEL_ERROR, + "trailing comma after enumerator list requires ISO C99 or later", 0}, + {E_OUTPUT_FILE_OPEN_ERR, ERROR_LEVEL_ERROR, + "Failed to open output file '%s' (%s)", 0}, + {E_INPUT_FILE_OPEN_ERR, ERROR_LEVEL_ERROR, + "Failed to open input file '%s' (%s)", 0}, + {W_SHIFT_NEGATIVE, ERROR_LEVEL_WARNING, "%s shift by negative amount", 0}, + {W_INVALID_STACK_LOCATION, ERROR_LEVEL_WARNING, + "access to invalid stack location", 0}, + {W_BINARY_INTEGER_CONSTANT_C23, ERROR_LEVEL_WARNING, + "binary integer constant requires C23 or later", 0}, }; /* ------------------------------------------------------------------------------- @@ -475,6 +560,10 @@ void setErrorLogLevel(ERROR_LOG_LEVEL level) { _SDCCERRG.logLevel = level; } * ------------------------------------------------------------------------------- */ int vwerror(int errNum, va_list marker) { + struct dbuf_s dbuf; + char *errmsg; + char *oldmsg; + if (_SDCCERRG.out == NULL) { _SDCCERRG.out = DEFAULT_ERROR_OUT; } @@ -490,6 +579,8 @@ int vwerror(int errNum, va_list marker) { return 0; } + dbuf_init(&dbuf, 200); + if ((ErrTab[errNum].errType >= _SDCCERRG.logLevel) && (!ErrTab[errNum].disabled)) { if (ErrTab[errNum].errType >= ERROR_LEVEL_ERROR || _SDCCERRG.werror) @@ -497,42 +588,50 @@ int vwerror(int errNum, va_list marker) { if (filename && lineno) { if (_SDCCERRG.style) - fprintf(_SDCCERRG.out, "%s(%d) : ", filename, lineno); + dbuf_printf(&dbuf, "%s(%d) : ", filename, lineno); else - fprintf(_SDCCERRG.out, "%s:%d: ", filename, lineno); + dbuf_printf(&dbuf, "%s:%d: ", filename, lineno); } else if (lineno) { - fprintf(_SDCCERRG.out, "at %d: ", lineno); + dbuf_printf(&dbuf, "at %d: ", lineno); } else { - fprintf(_SDCCERRG.out, "-:0: "); + dbuf_printf(&dbuf, "-:0: "); } switch (ErrTab[errNum].errType) { case ERROR_LEVEL_SYNTAX_ERROR: - fprintf(_SDCCERRG.out, "syntax error: "); + dbuf_printf(&dbuf, "syntax error: "); break; case ERROR_LEVEL_ERROR: - fprintf(_SDCCERRG.out, "error %d: ", errNum); + dbuf_printf(&dbuf, "error %d: ", errNum); break; case ERROR_LEVEL_WARNING: case ERROR_LEVEL_PEDANTIC: if (_SDCCERRG.werror) - fprintf(_SDCCERRG.out, "error %d: ", errNum); + dbuf_printf(&dbuf, "error %d: ", errNum); else - fprintf(_SDCCERRG.out, "warning %d: ", errNum); + dbuf_printf(&dbuf, "warning %d: ", errNum); break; case ERROR_LEVEL_INFO: - fprintf(_SDCCERRG.out, "info %d: ", errNum); + dbuf_printf(&dbuf, "info %d: ", errNum); break; default: break; } - vfprintf(_SDCCERRG.out, ErrTab[errNum].errText, marker); - fprintf(_SDCCERRG.out, "\n"); + dbuf_vprintf(&dbuf, ErrTab[errNum].errText, marker); + errmsg = dbuf_detach_c_str(&dbuf); + for (oldmsg = setFirstItem(_SDCCERRG.log); oldmsg; + oldmsg = setNextItem(_SDCCERRG.log)) + if (strcmp(errmsg, oldmsg) == 0) { + free(errmsg); + return 0; + } + addSetHead(&_SDCCERRG.log, errmsg); + fprintf(_SDCCERRG.out, "%s\n", errmsg); return 1; } else { /* Below the logging level, drop. */ @@ -553,6 +652,27 @@ int werror(int errNum, ...) { return ret; } +/* ------------------------------------------------------------------------------- +werror_bt - like werror(), but als provide a backtrace + * ------------------------------------------------------------------------------- + */ +int werror_bt(int errNum, ...) { +#ifdef HAVE_BACKTRACE_SYMBOLS_FD + void *callstack[16]; + int frames = backtrace(callstack, 16); + fprintf(stderr, "Backtrace:\n"); + backtrace_symbols_fd(callstack, frames, STDERR_FILENO); +#endif + + int ret; + va_list marker; + va_start(marker, errNum); + ret = vwerror(errNum, marker); + va_end(marker); + + return ret; +} + /* ------------------------------------------------------------------------------- * werrorfl - Output a standard error message with variable number of arguments. * Use a specified filename and line number instead of the default. @@ -606,6 +726,20 @@ void setWarningDisabled(int errNum) { ErrTab[errNum].disabled = 1; } +/* ------------------------------------------------------------------------------- + * disabledState - Enable/Disable output of specified warning + * ------------------------------------------------------------------------------- + */ +int setWarningDisabledState(int errNum, int disabled) { + if ((errNum >= 0) && (errNum < NELEM(ErrTab)) && + (ErrTab[errNum].errType <= ERROR_LEVEL_WARNING)) { + int originalState = ErrTab[errNum].disabled; + ErrTab[errNum].disabled = disabled; + return originalState; + } + return 0; +} + /* ------------------------------------------------------------------------------- * Set the flag to treat warnings as errors * ------------------------------------------------------------------------------- diff --git a/src/SDCCerr.h b/src/SDCCerr.h index 96da2fe37..93069fbf4 100644 --- a/src/SDCCerr.h +++ b/src/SDCCerr.h @@ -20,6 +20,7 @@ #define __SDCCERR_H +#include "SDCCset.h" #include #include @@ -56,8 +57,8 @@ enum { E_PTR_REQD = 27, /* pointer required */ E_UNARY_OP = 28, /* unary operator bad op*/ E_CONV_ERR = 29, /* conversion error */ - E_INT_REQD = 30, /* bit field must be int*/ - E_BITFLD_SIZE = 31, /* bit field size > 16 */ + E_BITFLD_TYPE = 30, /* invalid type for bit-field */ + E_BITFLD_SIZE = 31, /* bit-field too wide for type */ W_TRUNCATION = 32, /* high order trucation */ E_CODE_WRITE = 33, /* trying 2 write to code */ E_LVALUE_CONST = 34, /* lvalue is a const */ @@ -74,7 +75,7 @@ enum { E_BITWISE_OP = 45, /* bit op invalid op */ E_ANDOR_OP = 46, /* && || op invalid */ E_TYPE_MISMATCH = 47, /* type mismatch */ - E_AGGR_ASSIGN = 48, /* aggr assign */ + E_ARRAY_ASSIGN = 48, /* array assign */ E_ARRAY_DIRECT = 49, /* array indexing in */ E_BIT_ARRAY = 50, /* bit array not allowed */ E_DUPLICATE_TYPEDEF = 51, /* typedef name duplicate */ @@ -122,11 +123,11 @@ enum { W_DOUBLE_UNSUPPORTED = 93, /* 'double' not supported yet */ W_COMP_RANGE = 94, /* comparison is always %s due to limited range of data type */ - W_FUNC_NO_RETURN = 95, /* no return statement found */ - W_PRE_PROC_WARNING = 96, /* preprocessor generated warning */ - E_STRUCT_AS_ARG = 97, /* structure passed as argument */ - E_PREV_DEF_CONFLICT = 98, /* previous definition conflicts with current */ - E_CODE_NO_INIT = 99, /* vars in code space must have initializer */ + W_FUNC_NO_RETURN = 95, /* no return statement found */ + W_PRE_PROC_WARNING = 96, /* preprocessor generated warning */ + E_STRUCT_AS_ARG = 97, /* structure passed as argument */ + E_PREV_DECL_CONFLICT = 98, /* previous declaration conflicts with current */ + E_CODE_NO_INIT = 99, /* vars in code space must have initializer */ E_OPS_INTEGRAL = 100, /* operans must be integral for certain assignments */ E_TOO_MANY_PARMS = 101, /* too many parameters */ E_TOO_FEW_PARMS = 102, /* too few parameters */ @@ -146,7 +147,7 @@ enum { W_SHIFT_CHANGED = 116, /* shift changed to zero */ W_UNKNOWN_OPTION = 117, /* don't know the option */ W_UNSUPP_OPTION = 118, /* processor reset has been redifned */ - W_UNKNOWN_FEXT = 119, /* unknown file extension */ + E_UNKNOWN_FEXT = 119, /* unknown file extension */ W_TOO_MANY_SRC = 120, /* can only compile one .c file at a time */ I_CYCLOMATIC = 121, /* information message */ E_DIVIDE_BY_ZERO = 122, /* / 0 */ @@ -234,8 +235,9 @@ enum { E_FLEXARRAY_INEMPTYSTRCT = 189, /* flexible array in otherwise empty struct */ W_EMPTY_SOURCE_FILE = 190, /* ISO C forbids an empty source file */ W_BAD_PRAGMA_ARGUMENTS = - 191, /* #pragma %s: bad argument(s); pragma ignored */ - E_BAD_RESTRICT = 192, /* Only pointers may be qualified with 'restrict' */ + 191, /* #pragma %s: bad argument(s); pragma ignored */ + E_BAD_RESTRICT = + 192, /* Only object pointers may be qualified with 'restrict' */ E_BAD_INLINE = 193, /* Only functions may be qualified with 'inline' */ E_BAD_INT_ARGUMENT = 194, /* Bad integer option argument */ E_NEGATIVE_ARRAY_SIZE = 195, /* Size of array '%s' is negative */ @@ -248,8 +250,9 @@ enum { W_DEPRECATED_OPTION = 201, /* deprecated compiler option '%s' */ E_BAD_DESIGNATOR = 202, /* Bad designated initializer */ W_DUPLICATE_INIT = 203, /* duplicate initializer */ - E_INVALID_UNIVERSAL = 204, /* incomplete universal character name %s. */ - W_UNIVERSAL_C99 = 205, /* universal character names are only valid in C99 */ + E_INVALID_UNIVERSAL = 204, /* invalid universal character name %s. */ + W_UNIVERSAL_C95 = + 205, /* universal character names are only valid in C95 or later */ E_SHORTLONG = 206, /* Invalid combination of short / long */ E_INTEGERSUFFIX = 207, /* Invalid integer suffix */ E_AUTO_ADDRSPACE = 208, /* named address space for auto var */ @@ -266,6 +269,75 @@ enum { should not have happened, but can be handled */ W_UNRECOGNIZED_ASM = 218, /* unrecognized asm instruction in peephole optimizer */ + W_FLEXARRAY_INSTRUCT = 219, /* using flexible arrays in a struct */ + E_TYPE_IS_FUNCTION = 220, /* typedef void foo_t(void); foo_t foo; */ + W_INLINE_NAKED = 221, /* inline function is naked */ + E_Z88DK_FASTCALL_PARAMETERS = + 222, /* invalid number of parameters in __z88dk_fastcall */ + E_Z88DK_FASTCALL_PARAMETER = + 223, /* invalid parameter type in __z88dk_fastcall */ + W_REPEAT_QUALIFIER = 224, /* the same qualifier appears more than once */ + W_NO_TYPE_SPECIFIER = 225, /* type specifier missing in declaration */ + E_NO_TYPE_SPECIFIER = 226, /* type specifier missing in declaration */ + E_MULTIPLE_DEFAULT_IN_GENERIC = + 227, /* multiple default expressions in generic association */ + E_MULTIPLE_MATCHES_IN_GENERIC = + 228, /* multiple matching expressions in generic association */ + E_NO_MATCH_IN_GENERIC = + 229, /* no matching expression in generic association */ + W_LABEL_WITHOUT_STATEMENT = + 230, /* label without statement, not allowed in standard C */ + E_WCHAR_CONST_C95 = + 231, /* character constant of type wchar_t requires ISO C 95 or later */ + E_WCHAR_CONST_C11 = 232, /* character constant of type char16_t or char32_t + equires ISO C 11 or later */ + E_WCHAR_STRING_C95 = + 233, /* wide character string literal requires ISO C 95 or later */ + E_WCHAR_STRING_C11 = + 234, /* wide character string literal requires ISO C 11 or later */ + W_UNKNOWN_REG = 235, /* unknown register specified */ + E_HEXFLOAT_C99 = + 236, /* hexadecimal floating constant requires ISO C99 or later */ + E_ANONYMOUS_STRUCT_TAG = + 237, /* anonymous struct/union should not have a tag */ + W_INLINE_FUNCATTR = + 238, /* inline functions should not be z88dk_fastcall or z88dk_callee */ + E_FOR_INITAL_DECLARATION_C99 = + 239, /* initial declaration in for loop requires ISO C99 or later */ + E_QUALIFIED_ARRAY_PARAM_C99 = + 240, /* qualifiers in array parameters require ISO C99 or later */ + E_QUALIFIED_ARRAY_NOPARAM = + 241, /* qualifier or static in array declarator that is not a parameter */ + E_STATIC_ARRAY_PARAM_C99 = + 242, /* static in array parameters requires ISO C99 or later */ + E_INT_MULTIPLE = 243, /* multiple interrupt numbers */ + W_INCOMPAT_PTYPES = 244, /* incompatible pointer assignment (not allowed by + the standard, but allowed in SDCC) */ + E_STATIC_ASSERTION_C2X = + 245, /* static assertion with one argument requires C2X or later */ + W_STATIC_ASSERTION_2 = 246, /* static assertion failed */ + E_DECL_AFTER_STATEMENT_C99 = + 247, /* declaration after statement requires ISO C99 or later */ + E_SHORTCALL_INVALID_VALUE = + 248, /* Invalid value for a __z88dk_shortcall specifier */ + E_DUPLICATE_PARAMTER_NAME = 249, /* duplicate parameter name */ + E_AUTO_FILE_SCOPE = 250, /* auto in declaration at file scope */ + E_U8_CHAR_C2X = 251, /* u8 character constant requires ISO C2X or later */ + E_U8_CHAR_INVALID = 252, /* invalid u8 character constant */ + E_ATTRIBUTE_C2X = 253, /* attribute requires ISO C2X or later */ + E_COMPOUND_LITERALS_C99 = + 254, /* compound literals require ISO C99 or later */ + E_THREAD_LOCAL = 255, /* thread-local storage is not implemented */ + E_ENUM_COMMA_C99 = + 256, /* trailing comma after enumerator list requires ISO C99 or later */ + E_OUTPUT_FILE_OPEN_ERR = + 257, /* Failed to open output file for writing (with error message) */ + E_INPUT_FILE_OPEN_ERR = + 258, /* Failed to open input file for readin (with error message) */ + W_SHIFT_NEGATIVE = 259, /* shift by negative amount */ + W_INVALID_STACK_LOCATION = 260, /* access to invalid stack location */ + W_BINARY_INTEGER_CONSTANT_C23 = + 261, /* binary integer constant requires ISO C23 or later */ /* don't touch this! */ NUMBER_OF_ERROR_MESSAGES /* Number of error messages */ @@ -282,6 +354,11 @@ enum { ((expr) ? (void)0 : fatal(1, E_INTERNAL_ERROR, __FILE__, __LINE__, #expr)) #endif +#define wassertl_bt(a, s) \ + (void)((a) ? 0 : (werror_bt(E_INTERNAL_ERROR, __FILE__, __LINE__, s), 0)) + +#define wassert_bt(a) wassertl_bt(a, "code generator internal error") + /** Describes the maximum error level that will be logged. Any level * includes all of the levels listed after it. * @@ -310,6 +387,7 @@ struct SDCCERRG { FILE *out; int style; /* 1=MSVC */ int werror; /* treat the warnings as errors */ + set *log; }; extern struct SDCCERRG _SDCCERRG; @@ -346,6 +424,15 @@ werror - Output a standard eror message with variable number of arguements int werror(int errNum, ...); +/* +------------------------------------------------------------------------------- +werror_bt - like werror(), but als provide a backtrace + +------------------------------------------------------------------------------- +*/ + +int werror_bt(int errNum, ...); + /* ------------------------------------------------------------------------------- werrorfl - Output a standard eror message with variable number of arguements. @@ -381,6 +468,14 @@ disabled - Disable output of specified warning void setWarningDisabled(int errNum); +/* +------------------------------------------------------------------------------- +disabledState - Enable/Disable output of specified warning +------------------------------------------------------------------------------- +*/ + +int setWarningDisabledState(int errNum, int disabled); + /* ------------------------------------------------------------------------------- Set the flag to treat warnings as errors diff --git a/src/SDCCgen.c b/src/SDCCgen.c index a0bc55663..516029aee 100644 --- a/src/SDCCgen.c +++ b/src/SDCCgen.c @@ -87,17 +87,7 @@ static void add_line_node(const char *line) { pl = Safe_alloc(sizeof(lineNode)); -#if 1 memcpy(pl, (lineElem_t *)&genLine.lineElement, sizeof(lineElem_t)); -#else - pl->ic = genLine.lineElement.ic; - pl->isInline = genLine.lineElement.isInline; - pl->isComment = genLine.lineElement.isComment; - pl->isDebug = genLine.lineElement.isDebug; - pl->isLabel = genLine.lineElement.isLabel; - pl->visited = genLine.lineElement.visited; - pl->aln = genLine.lineElement.aln; -#endif pl->line = Safe_strdup(line); @@ -167,7 +157,7 @@ void emitcode(const char *inst, const char *fmt, ...) { va_end(ap); } -void emitLabel(symbol *tlbl) { +void emitLabel(const symbol *tlbl) { if (!tlbl) return; emitcode("", "!tlabeldef", labelKey2num(tlbl->key)); @@ -180,6 +170,8 @@ void emitLabel(symbol *tlbl) { void genInline(iCode *ic) { char *buf, *bp, *begin; bool inComment = FALSE; + bool inLiteral = FALSE; + bool inLiteralString = FALSE; D(emitcode(";", "genInline")); @@ -190,13 +182,41 @@ void genInline(iCode *ic) { /* Emit each line as a code */ while (*bp) { switch (*bp) { + case '\'': + inLiteral = !inLiteral; + ++bp; + break; + + case '"': + inLiteralString = !inLiteralString; + ++bp; + break; + case ';': - inComment = TRUE; + if (!inLiteral && !inLiteralString) { + inComment = TRUE; + } ++bp; break; + case ':': + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && !inLiteral && !inLiteralString && + (isspace((unsigned char)bp[1]))) { + ++bp; + *bp = '\0'; + ++bp; + emitcode(begin, NULL); + begin = bp; + } else { + ++bp; + } + break; + case '\x87': case '\n': + inLiteral = FALSE; + inLiteralString = FALSE; inComment = FALSE; *bp++ = '\0'; @@ -211,15 +231,7 @@ void genInline(iCode *ic) { break; default: - /* Add \n for labels, not dirs such as c:\mydir */ - if (!inComment && (*bp == ':') && (isspace((unsigned char)bp[1]))) { - ++bp; - *bp = '\0'; - ++bp; - emitcode(begin, NULL); - begin = bp; - } else - ++bp; + ++bp; break; } } @@ -276,7 +288,7 @@ void printLine(lineNode *head, struct dbuf_s *oBuf) { /*-----------------------------------------------------------------*/ /* ifxForOp - returns the icode containing the ifx for operand */ /*-----------------------------------------------------------------*/ -iCode *ifxForOp(operand *op, const iCode *ic) { +iCode *ifxForOp(const operand *op, const iCode *ic) { iCode *ifxIc; /* if true symbol then needs to be assigned */ @@ -289,7 +301,8 @@ iCode *ifxForOp(operand *op, const iCode *ic) { ; if (ifxIc && ifxIc->op == IFX && IC_COND(ifxIc)->key == op->key && - OP_SYMBOL(op)->liveTo <= ifxIc->seq) + OP_SYMBOL_CONST(op)->liveFrom >= ic->seq && + OP_SYMBOL_CONST(op)->liveTo <= ifxIc->seq) return ifxIc; } diff --git a/src/SDCCgen.h b/src/SDCCgen.h index a3748d395..8f43b94f0 100644 --- a/src/SDCCgen.h +++ b/src/SDCCgen.h @@ -47,6 +47,10 @@ typedef struct lineElem_s { } lineElem_t; typedef struct lineNode_s { +#if UNNAMED_STRUCT_TAG + struct lineElem_s; +#else + /* exactly the same members as of struct lineElem_s */ char *line; iCode *ic; unsigned int isInline : 1; @@ -55,6 +59,7 @@ typedef struct lineNode_s { unsigned int isLabel : 1; unsigned int visited : 1; asmLineNodeBase *aln; +#endif struct lineNode_s *prev; struct lineNode_s *next; } lineNode; @@ -81,10 +86,10 @@ const char *format_opcode(const char *inst, const char *fmt, va_list ap); void emit_raw(const char *line); void va_emitcode(const char *inst, const char *fmt, va_list ap); void emitcode(const char *inst, const char *fmt, ...); -void emitLabel(symbol *tlbl); +void emitLabel(const symbol *tlbl); void genInline(iCode *ic); void printLine(lineNode *, struct dbuf_s *); -iCode *ifxForOp(operand *op, const iCode *ic); +iCode *ifxForOp(const operand *op, const iCode *ic); #ifdef __cplusplus } diff --git a/src/SDCCglobl.h b/src/SDCCglobl.h index 669753a43..acbc78b8e 100644 --- a/src/SDCCglobl.h +++ b/src/SDCCglobl.h @@ -21,6 +21,7 @@ #ifndef SDCCGLOBL_H #define SDCCGLOBL_H +#include #include #include #include @@ -28,33 +29,15 @@ #include #ifndef __cplusplus -#ifndef _MSC_VER #include -#ifndef TRUE -#define TRUE true -#endif -#ifndef FALSE -#define FALSE false -#endif -#else -typedef unsigned char bool; -#define true 1 -#define false 0 -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif #endif -#else + #ifndef TRUE #define TRUE true #endif #ifndef FALSE #define FALSE false #endif -#endif #include "SDCCset.h" @@ -115,7 +98,6 @@ typedef unsigned char bool; #include "SDCCerr.h" #define SPACE ' ' -#define ZERO 0 #include /* PATH_MAX */ #if !defined(PATH_MAX) || (PATH_MAX < 2048) @@ -143,24 +125,21 @@ typedef unsigned char bool; #define THROW_BOTH 3 #endif -/* size's in bytes */ +/* sizes in bytes */ #define BOOLSIZE port->s.char_size #define CHARSIZE port->s.char_size #define SHORTSIZE port->s.short_size #define INTSIZE port->s.int_size #define LONGSIZE port->s.long_size #define LONGLONGSIZE port->s.longlong_size -#define PTRSIZE port->s.ptr_size -#define FPTRSIZE port->s.fptr_size -#define GPTRSIZE port->s.gptr_size +#define NEARPTRSIZE port->s.near_ptr_size +#define FARPTRSIZE port->s.far_ptr_size +#define GPTRSIZE port->s.ptr_size +#define FUNCPTRSIZE port->s.funcptr_size +#define BFUNCPTRSIZE port->s.banked_funcptr_size #define BITSIZE port->s.bit_size #define FLOATSIZE port->s.float_size -#define MAXBASESIZE port->s.max_base_size -#define SMALL_MODEL 0 -#define LARGE_MODEL 1 - -#define MAX_TVAR 6 #define INITIAL_INLINEASM (4 * 1024) #define DEFPOOLSTACK(type, size) \ type *type##Pool; \ @@ -186,12 +165,6 @@ typedef unsigned char bool; t_##stack stack[size]; \ t_##stack(*p_##stack) = stack - 1; -/* define extern stack */ -#define EXTERN_STACK_DCL(stack, type, size) \ - typedef type t_##stack; \ - extern t_##stack stack[size]; \ - extern t_##stack *p_##stack; - #define STACK_EMPTY(stack) ((p_##stack) < stack) #define STACK_FULL(stack) \ ((p_##stack) >= (stack + sizeof(stack) / sizeof(*stack) - 1)) @@ -213,11 +186,11 @@ typedef unsigned char bool; (fatal(1, E_STACK_VIOLATION, #stack, \ (o < 0) ? "underflow" : (o > 0) ? "overflow" : "empty")) +/* for semantically partitioned nest level values */ +#define LEVEL_UNIT 65536 +#define SUBLEVEL_UNIT 1 + /* optimization options */ -/* - * cloneOptimize function in SDCC.lex should be updated every time - * a new set is added to the optimize structure! - */ struct optimize { int global_cse; int ptrArithmetic; @@ -227,12 +200,27 @@ struct optimize { int label4; int loopInvariant; int loopInduction; - int noJTabBoundary; int noLoopReverse; int codeSpeed; int codeSize; int lospre; - int lospre_unsafe_read; + int allow_unsafe_read; + int noStdLibCall; +}; + +/** Build model. + Used in options.model.A bit each as port.supported_models is an OR + of these. +*/ +enum { + NO_MODEL = 0, /* no model applicable */ + MODEL_SMALL = 1, + MODEL_COMPACT = 2, + MODEL_MEDIUM = 4, + MODEL_LARGE = 8, + MODEL_FLAT24 = 16, + // MODEL_PAGE0 = 32, /* disabled, was for the xa51 port */ + MODEL_HUGE = 64 /* for banked support */ }; /* overlay segment name and the functions @@ -243,12 +231,19 @@ typedef struct { char *funcs[128]; /* function name that belong to this */ } olay; +enum { + NO_DEPENDENCY_FILE_OPT = 0, + SYSTEM_DEPENDENCY_FILE_OPT = 1, + USER_DEPENDENCY_FILE_OPT = 2 +}; + /* other command line options */ /* * cloneOptions function in SDCC.lex should be updated every time * a new set is added to the options structure! */ struct options { + int model; /* see MODEL_* defines above */ int stackAuto; /* Stack Automatic */ int useXstack; /* use Xternal Stack */ int stack10bit; /* use 10 bit stack (flat24 model only) */ @@ -274,7 +269,6 @@ struct options { int nostdinc; /* Don't use standard include files */ int noRegParams; /* Disable passing some parameters in registers */ int verbose; /* Show what the compiler is doing */ - int shortis8bits; /* treat short like int or char */ int lessPedantic; /* disable some warnings */ int profile; /* Turn on extra profiling information */ int omitFramePtr; /* Turn off the frame pointer. */ @@ -286,11 +280,9 @@ struct options { int protect_sp_update; /* DS390 - will disable interrupts during ESP:SP updates */ int parms_in_bank1; /* DS390 - use reg bank1 to pass parameters */ - int stack_size; /* MCS51/DS390 - Tells the linker to allocate this space for - stack */ - int no_pack_iram; /* MCS51/DS390 - Deprecated: Tells the linker not to pack - variables in internal ram */ - int acall_ajmp; /* MCS51 - Use acall/ajmp instead of lcall/ljmp */ + int stack_size; /* MCS51/DS390 - Tells the linker to allocate this space for + stack */ + int acall_ajmp; /* MCS51 - Use acall/ajmp instead of lcall/ljmp */ int no_ret_without_call; /* MCS51 - Do not use ret independent of acall/lcall */ int use_non_free; /* Search / include non-free licensed libraries and header @@ -317,14 +309,17 @@ struct options { studio */ int use_stdout; /* send errors to stdout instead of stderr */ int no_std_crt0; /* for the z80/gbz80 do not link default crt0.o*/ + int std_c95; /* enable C95 keywords/constructs */ int std_c99; /* enable C99 keywords/constructs */ int std_c11; /* enable C11 keywords/constructs */ + int std_c2x; /* enable C2X keywords/constructs */ int std_sdcc; /* enable SDCC extensions to C */ int dollars_in_ident; /* zero means dollar signs are punctuation */ - int unsigned_char; /* use unsigned for char without signed/unsigned modifier - */ - char *code_seg; /* segment name to use instead of CSEG */ - char *const_seg; /* segment name to use instead of CONST */ + int signed_char; /* use signed for char without signed/unsigned modifier */ + char *code_seg; /* segment name to use instead of CSEG */ + char *const_seg; /* segment name to use instead of CONST */ + char *data_seg; /* segment name to use instead of DATA */ + int dependencyFileOpt; /* write dependencies to given file */ /* sets */ set *calleeSavesSet; /* list of functions using callee save */ set *excludeRegsSet; /* registers excluded from saving */ @@ -356,16 +351,15 @@ extern int seqPointNo; /* current sequence point */ extern FILE *yyin; /* */ extern FILE *asmFile; /* assembly output file */ extern FILE *cdbFile; /* debugger symbol file */ -extern int NestLevel; /* NestLevel SDCC.y */ +extern long NestLevel; /* NestLevel SDCC.y */ extern int stackPtr; /* stack pointer SDCC.y */ extern int xstackPtr; /* external stack pointer SDCC.y */ -extern int reentrant; /* /X flag has been sent SDCC.y */ extern char buffer[PATH_MAX * 2]; /* general buffer SDCCmain.c */ extern int currRegBank; /* register bank being used SDCCgens.c */ extern int RegBankUsed[4]; /* JCF: register banks used SDCCmain.c */ extern int BitBankUsed; /* MB: overlayable bit bank SDCCmain.c */ extern struct symbol *currFunc; /* current function SDCCgens.c */ -extern int cNestLevel; /* block nest level SDCCval.c */ +extern long cNestLevel; /* block nest level SDCCval.c */ extern int blockNo; /* maximum sequential block number */ extern int currBlockno; /* sequential block number */ extern struct optimize optimize; @@ -386,9 +380,7 @@ void setParseWithComma(set **, const char *); system. */ #define wassertl(a, s) \ - if (!(a)) { \ - werror(E_INTERNAL_ERROR, __FILE__, __LINE__, s); \ - } + (void)((a) ? 0 : (werror(E_INTERNAL_ERROR, __FILE__, __LINE__, s), 0)) #define wassert(a) wassertl(a, "code generator internal error") @@ -406,7 +398,8 @@ enum { DUMP_PACK, DUMP_RASSGN, DUMP_LRANGE, - DUMP_LOSPRE + DUMP_LOSPRE, + DUMP_CUSTOM /* For temporary dump points */ }; struct _dumpFiles { diff --git a/src/SDCCglue.c b/src/SDCCglue.c index 5c9aed09f..b62340865 100644 --- a/src/SDCCglue.c +++ b/src/SDCCglue.c @@ -37,6 +37,9 @@ symbol *interrupts[INTNO_MAX + 1]; void printIval(symbol *, sym_link *, initList *, struct dbuf_s *, bool check); set *publics = NULL; /* public variables */ set *externs = NULL; /* Variables that are declared as extern */ +set *strSym = NULL; /* string initializers */ +set *ccpStr = + NULL; /* char * const pointers with a string literal initializer */ unsigned maxInterrupts = 0; int allocInfo = 1; @@ -64,7 +67,7 @@ char *aopLiteralGptr(const char *name, value *val) { return dbuf_detach_c_str(&dbuf); } -char *aopLiteralLong(value *val, int offset, int size) { +const char *aopLiteralLong(value *val, int offset, int size) { unsigned long v; struct dbuf_s dbuf; @@ -102,7 +105,7 @@ char *aopLiteralLong(value *val, int offset, int size) { /*-----------------------------------------------------------------*/ /* aopLiteral - string from a literal value */ /*-----------------------------------------------------------------*/ -char *aopLiteral(value *val, int offset) { +const char *aopLiteral(value *val, int offset) { return aopLiteralLong(val, offset, 1); } @@ -118,11 +121,17 @@ static void emitRegularMap(memmap *map, bool addPublics, bool arFlag) { if (addPublics) { /* PENDING: special case here - should remove */ - if (!strcmp(map->sname, CODE_NAME)) - dbuf_tprintf(&map->oBuf, "\t!areacode\n", map->sname); - else if (!strcmp(map->sname, DATA_NAME)) - dbuf_tprintf(&map->oBuf, "\t!areadata\n", map->sname); - else if (!strcmp(map->sname, HOME_NAME)) + if (!strcmp(map->sname, CODE_NAME)) { + if (options.code_seg && strcmp(CODE_NAME, options.code_seg)) + dbuf_tprintf(&map->oBuf, "\t!areacode\n", options.code_seg); + else + dbuf_tprintf(&map->oBuf, "\t!areacode\n", map->sname); + } else if (!strcmp(map->sname, DATA_NAME)) { + if (options.data_seg && strcmp(DATA_NAME, options.data_seg)) + dbuf_tprintf(&map->oBuf, "\t!areadata\n", options.data_seg); + else + dbuf_tprintf(&map->oBuf, "\t!areadata\n", map->sname); + } else if (!strcmp(map->sname, HOME_NAME)) dbuf_tprintf(&map->oBuf, "\t!areahome\n", map->sname); else dbuf_tprintf(&map->oBuf, "\t!area\n", map->sname); @@ -178,6 +187,33 @@ static void emitRegularMap(memmap *map, bool addPublics, bool arFlag) { SPEC_OCLS(sym->etype) == initialized) && !SPEC_ABSA(sym->etype) && !SPEC_ADDRSPACE(sym->etype)) { sym_link *t; + ast *ival = NULL; + symbol *tsym = copySymbol(sym); + + // check for constant + if (IS_AGGREGATE(tsym->type)) { + ival = initAggregates(tsym, tsym->ival, NULL); + } else { + if (getNelements(tsym->type, tsym->ival) > 1) { + werrorfl(tsym->fileDef, tsym->lineDef, W_EXCESS_INITIALIZERS, + "scalar", tsym->name); + } + ival = newNode('=', newAst_VALUE(symbolVal(tsym)), + decorateType(resolveSymbols(list2expr(tsym->ival)), + RESULT_TYPE_NONE)); + } + if (ival) { + // set ival's lineno to where the symbol was defined + setAstFileLine(ival, filename = tsym->fileDef, + lineno = tsym->lineDef); + // check if this is not a constant expression + if (!constExprTree(ival)) { + werrorfl(ival->filename, ival->lineno, E_CONST_EXPECTED, + "found expression"); + // but try to do it anyway + } + } + /* create a new "XINIT (CODE)" symbol, that will be emitted later in the static seg */ newSym = copySymbol(sym); @@ -197,10 +233,10 @@ static void emitRegularMap(memmap *map, bool addPublics, bool arFlag) { DCL_PTR_CONST(t) = 1; SPEC_STAT(newSym->etype) = 1; - /* This results in unencessary calls to stringToSymbol() through - * decorateType(), which make strings take twice as much space as they - * should */ + strSym = NULL; + ++noAlloc; resolveIvalSym(newSym->ival, newSym->type); + --noAlloc; // add it to the "XINIT (CODE)" segment addSet((SPEC_OCLS(sym->etype) == xidata) ? &xinit->syms @@ -209,8 +245,10 @@ static void emitRegularMap(memmap *map, bool addPublics, bool arFlag) { if (!SPEC_ABSA(sym->etype)) { struct dbuf_s tmpBuf; + symbol *ps = NULL; + set *tmpSym = NULL; - dbuf_init(&tmpBuf, 4096); + wassert(dbuf_init(&tmpBuf, 4096)); // before allocation we must parse the sym->ival tree // but without actually generating initialization code ++noAlloc; @@ -219,8 +257,21 @@ static void emitRegularMap(memmap *map, bool addPublics, bool arFlag) { printIval(sym, sym->type, sym->ival, &tmpBuf, TRUE); --noInit; --noAlloc; + + // delete redundant __str_%d symbols (initializer for char arrays) + for (ps = setFirstItem(statsg->syms); ps; + ps = setNextItem(statsg->syms)) + if (!strstr(tmpBuf.buf, ps->name) && isinSet(strSym, ps)) + addSet(&tmpSym, ps); + for (ps = setFirstItem(tmpSym); ps; ps = setNextItem(tmpSym)) + deleteSetItem(&statsg->syms, ps); + + deleteSet(&tmpSym); dbuf_destroy(&tmpBuf); } + + if (strSym) + deleteSet(&strSym); } else { if (IS_AGGREGATE(sym->type)) { ival = initAggregates(sym, sym->ival, NULL); @@ -336,7 +387,13 @@ value *initValPointer(ast *expr) { IS_ADDRESS_OF_OP(expr->left->left)) { return valForStructElem(expr->left->left->left, expr->left->right); } + + /* case 2.1. a->b */ + if (IS_AST_OP(expr->left) && expr->left->opval.op == PTR_OP) { + return valForStructElem(expr->left->left, expr->left->right); + } } + /* case 3. (((char *) &a) +/- constant) */ if (IS_AST_OP(expr) && (expr->opval.op == '+' || expr->opval.op == '-') && IS_CAST_OP(expr->left) && IS_ADDRESS_OF_OP(expr->left->right) && @@ -344,6 +401,7 @@ value *initValPointer(ast *expr) { return valForCastAggr(expr->left->right->left, expr->left->left->opval.lnk, expr->right, expr->opval.op); } + /* case 4. (array type) */ if (IS_AST_SYM_VALUE(expr) && IS_ARRAY(expr->ftype)) { STORAGE_CLASS sclass = SPEC_SCLS(expr->etype); @@ -384,7 +442,29 @@ value *initValPointer(ast *expr) { /* case 6. a->b ; some_struct->element */ if (IS_AST_OP(expr) && expr->opval.op == PTR_OP) { - return valForStructElem(expr->left->left, expr->right); + ast *t = expr->left; + + /*if (expr->left->left) + if (expr->left->left->left) + if (expr->left->left->opval.op == '[') + t = expr->left->left; + else + t = expr->left->left->left; + else + t = expr->left->left; + else + t = expr->left;*/ + + /* a more generic way of above code */ + while (t->left != NULL && t->opval.op != '[') + t = t->left; + + return valForStructElem(t, expr->right); + } + + /* case 7. function name */ + if (IS_AST_SYM_VALUE(expr) && IS_FUNC(expr->ftype)) { + return AST_VALUE(expr); } return NULL; @@ -393,18 +473,21 @@ value *initValPointer(ast *expr) { /*-----------------------------------------------------------------*/ /* initPointer - pointer initialization code massaging */ /*-----------------------------------------------------------------*/ -value *initPointer(initList *ilist, sym_link *toType) { +value *initPointer(initList *ilist, sym_link *toType, int showError) { value *val; ast *expr; if (!ilist) { - return valCastLiteral(toType, 0.0); + return valCastLiteral(toType, 0.0, 0); } expr = list2expr(ilist); - if (!expr) - goto wrong; + if (!expr) { + if (showError) + werror(E_CONST_EXPECTED); + return 0; + } /* try it the old way first */ if ((val = constExprValue(expr, FALSE))) @@ -421,7 +504,7 @@ value *initPointer(initList *ilist, sym_link *toType) { /* (char *)(expr1) */ if (IS_CAST_OP(expr)) { - if (compareType(toType, expr->left->ftype) == 0) { + if (compareType(toType, expr->left->ftype) == 0 && showError) { werror(W_INIT_WRONG); printFromToType(expr->left->ftype, toType); } @@ -436,28 +519,29 @@ value *initPointer(initList *ilist, sym_link *toType) { } else { val = initValPointer(expr); } + if (val) return val; -wrong: - if (expr) - werrorfl(expr->filename, expr->lineno, E_INCOMPAT_PTYPES); - else - werror(E_INCOMPAT_PTYPES); - return NULL; + if (showError) { + if (expr) + werrorfl(expr->filename, expr->lineno, E_CONST_EXPECTED); + else + werror(E_CONST_EXPECTED); + } + return 0; } /*-----------------------------------------------------------------*/ -/* printChar - formats and prints a characater string with DB */ +/* printChar - formats and prints a UTF-8 character string with DB */ /*-----------------------------------------------------------------*/ void printChar(struct dbuf_s *oBuf, const char *s, int plen) { int i; - int len = plen; int pplen = 0; char buf[100]; char *p = buf; - while (len && pplen < plen) { + while (pplen < plen) { i = 60; while (i && pplen < plen) { if (!isprint((unsigned char)*s) || *s == '\"' || *s == '\\') { @@ -466,24 +550,53 @@ void printChar(struct dbuf_s *oBuf, const char *s, int plen) { dbuf_tprintf(oBuf, "\t!ascii\n", buf); dbuf_tprintf(oBuf, "\t!db !constbyte\n", (unsigned char)*s); p = buf; + i = 60; } else { *p = *s; p++; + i--; } s++; pplen++; - i--; } if (p != buf) { *p = '\0'; dbuf_tprintf(oBuf, "\t!ascii\n", buf); p = buf; + i = 60; } + } +} - if (len > 60) - len -= 60; - else - len = 0; +/*-----------------------------------------------------------------*/ +/* printChar16 - formats and prints a UTF-16 character string with DB*/ +/*-----------------------------------------------------------------*/ +void printChar16(struct dbuf_s *oBuf, const TYPE_TARGET_UINT *s, int plen) { + int pplen = 0; + + while (pplen < plen) { + dbuf_printf(oBuf, "\t.byte %d,%d\n", (*s >> 0) & 0xff, (*s >> 8) & 0xff); + s++; + pplen++; + } + while (pplen < plen) { + dbuf_tprintf(oBuf, "\t!db !constbyte\n", 0); + pplen++; + } +} + +/*-----------------------------------------------------------------*/ +/* printChar32 - formats and prints a UTF-32 character string with DB*/ +/*-----------------------------------------------------------------*/ +void printChar32(struct dbuf_s *oBuf, const TYPE_TARGET_ULONG *s, int plen) { + int pplen = 0; + + while (pplen < plen) { + dbuf_printf(oBuf, "\t.byte %d,%d,%d,%d\n", (*s >> 0) & 0xff, + (*s >> 8) & 0xff, (*s >> 16) & 0xff, (*s >> 24) & 0xff); + + s++; + pplen++; } while (pplen < plen) { dbuf_tprintf(oBuf, "\t!db !constbyte\n", 0); @@ -519,27 +632,55 @@ int pointerTypeToGPByte(const int p_type, const char *iname, return -1; } +/*-----------------------------------------------------------------*/ +/* printIvalVal - generate ival according from value */ +/*-----------------------------------------------------------------*/ +static void printIvalVal(struct dbuf_s *oBuf, value *val, int size, + bool newline) { + if (size == 2 && port->use_dw_for_init) { + dbuf_tprintf(oBuf, "\t!dws\n", aopLiteralLong(val, 0, 2)); + return; + } + + const bool use_ret = false; + + if (!use_ret) + dbuf_printf(oBuf, "\t.byte "); + + for (int i = 0; i < size; i++) { + const char *inst; + const char *byte = aopLiteral(val, port->little_endian ? i : size - 1 - i); + if (use_ret) + inst = i != size - 1 ? "\tret %s\n" : "\tret %s"; + else + inst = i != size - 1 ? "%s, " : "%s"; + dbuf_printf(oBuf, inst, byte); + } + if (newline) + dbuf_printf(oBuf, "\n"); +} + /*-----------------------------------------------------------------*/ /* _printPointerType - generates ival for pointer type */ /*-----------------------------------------------------------------*/ static void _printPointerType(struct dbuf_s *oBuf, const char *name, int size) { if (size == 4) { if (port->little_endian) - dbuf_printf(oBuf, "\t.byte %s,(%s >> 8),(%s >> 16),(%s >> 24)", name, + dbuf_printf(oBuf, "\t.byte %s, (%s >> 8), (%s >> 16), (%s >> 24)", name, name, name, name); else - dbuf_printf(oBuf, "\t.byte (%s >> 24),(%s >> 16),(%s >> 8),%s", name, + dbuf_printf(oBuf, "\t.byte (%s >> 24), (%s >> 16), (%s >> 8), %s", name, name, name, name); } else if (size == 3) { if (port->little_endian) - dbuf_printf(oBuf, "\t.byte %s,(%s >> 8),(%s >> 16)", name, name, name); + dbuf_printf(oBuf, "\t.byte %s, (%s >> 8), (%s >> 16)", name, name, name); else - dbuf_printf(oBuf, "\t.byte (%s >> 16),(%s >> 8),%s", name, name, name); + dbuf_printf(oBuf, "\t.byte (%s >> 16), (%s >> 8), %s", name, name, name); } else { if (port->little_endian) - dbuf_printf(oBuf, "\t.byte %s,(%s >> 8)", name, name); + dbuf_printf(oBuf, "\t.byte %s, (%s >> 8)", name, name); else - dbuf_printf(oBuf, "\t.byte (%s >> 8),%s", name, name); + dbuf_printf(oBuf, "\t.byte (%s >> 8), %s", name, name); } } @@ -547,7 +688,7 @@ static void _printPointerType(struct dbuf_s *oBuf, const char *name, int size) { /* printPointerType - generates ival for pointer type */ /*-----------------------------------------------------------------*/ static void printPointerType(struct dbuf_s *oBuf, const char *name) { - _printPointerType(oBuf, name, 2); + _printPointerType(oBuf, name, (options.model == MODEL_FLAT24) ? 3 : 2); dbuf_printf(oBuf, "\n"); } @@ -557,9 +698,10 @@ static void printPointerType(struct dbuf_s *oBuf, const char *name) { static void printGPointerType(struct dbuf_s *oBuf, const char *iname, const char *oname, int type) { int byte = pointerTypeToGPByte(type, iname, oname); - int size = 2; + int size = (options.model == MODEL_FLAT24) ? 3 : 2; if (byte == -1) { _printPointerType(oBuf, iname, size + 1); + dbuf_printf(oBuf, "\n"); } else { _printPointerType(oBuf, iname, size); dbuf_printf(oBuf, ",#0x%02x\n", byte); @@ -578,10 +720,38 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, if (ilist && (ilist->type == INIT_DEEP)) ilist = ilist->init.deep; - if (!(val = list2val(ilist))) { - // assuming a warning has been thrown - val = constCharVal(0); + if (!(val = list2val(ilist, FALSE))) { + if (!!(val = initPointer(ilist, type, 0))) { + int i, size = getSize(type), le = port->little_endian, + top = (options.model == MODEL_FLAT24) ? 3 : 2; + ; + dbuf_printf(oBuf, "\t.byte "); + for (i = (le ? 0 : size - 1); le ? (i < size) : (i > -1); + i += (le ? 1 : -1)) { + if (i == 0) + if (val->name && strlen(val->name) > 0) + dbuf_printf(oBuf, "%s", val->name); + else + dbuf_printf(oBuf, "#0x00"); + else if (0 < i && i < top) + if (val->name && strlen(val->name) > 0) + dbuf_printf(oBuf, "(%s >> %d)", val->name, i * 8); + else + dbuf_printf(oBuf, "#0x00"); + else + dbuf_printf(oBuf, "#0x00"); + if (i == (le ? (size - 1) : 0)) + dbuf_printf(oBuf, "\n"); + else + dbuf_printf(oBuf, ", "); + } + return; + } else { + werrorfl(ilist->filename, ilist->lineno, E_CONST_EXPECTED); + val = constCharVal(0); + } } + if (val->etype && SPEC_SCLS(val->etype) != S_LITERAL) { werrorfl(ilist->filename, ilist->lineno, E_CONST_EXPECTED); val = constCharVal(0); @@ -593,7 +763,8 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, } if (val->type != type) { - val = valCastLiteral(type, floatFromVal(val)); + val = valCastLiteral(type, floatFromVal(val), + (TYPE_TARGET_ULONGLONG)ullFromVal(val)); } if (IS_INTEGRAL(val->type)) @@ -619,15 +790,10 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, case 2: if (port->use_dw_for_init) { - dbuf_tprintf(oBuf, "\t!dws\n", aopLiteralLong(val, 0, 2)); + printIvalVal(oBuf, val, 2, true); break; - } else if (port->little_endian) { - dbuf_printf(oBuf, "\t.byte %s,%s", aopLiteral(val, 0), - aopLiteral(val, 1)); - } else { - dbuf_printf(oBuf, "\t.byte %s,%s", aopLiteral(val, 1), - aopLiteral(val, 0)); } + printIvalVal(oBuf, val, 2, false); if (IS_UNSIGNED(val->type)) dbuf_printf(oBuf, "\t; %u\n", (unsigned int)ulVal); else @@ -639,13 +805,8 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, dbuf_tprintf(oBuf, "\t!dw !constword\n", 0); dbuf_tprintf(oBuf, "\t!dw !constword\n", 0); } else { - if (port->little_endian) { - dbuf_printf(oBuf, "\t.byte %s,%s,%s,%s", aopLiteral(val, 0), - aopLiteral(val, 1), aopLiteral(val, 2), aopLiteral(val, 3)); - } else { - dbuf_printf(oBuf, "\t.byte %s,%s,%s,%s", aopLiteral(val, 3), - aopLiteral(val, 2), aopLiteral(val, 1), aopLiteral(val, 0)); - } + printIvalVal(oBuf, val, 4, false); + if (IS_FLOAT(val->type)) { dbuf_printf(oBuf, "\t; % e\n", floatFromVal(val)); } else { @@ -657,20 +818,9 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, } break; case 8: - if (port->little_endian) { - dbuf_printf(oBuf, "\t.byte %s,%s,%s,%s,%s,%s,%s,%s", aopLiteral(val, 0), - aopLiteral(val, 1), aopLiteral(val, 2), aopLiteral(val, 3), - aopLiteral(val, 4), aopLiteral(val, 5), aopLiteral(val, 6), - aopLiteral(val, 7)); - } else { - dbuf_printf(oBuf, "\t.byte %s,%s,%s,%s,%s,%s,%s,%s", aopLiteral(val, 7), - aopLiteral(val, 6), aopLiteral(val, 5), aopLiteral(val, 4), - aopLiteral(val, 3), aopLiteral(val, 2), aopLiteral(val, 1), - aopLiteral(val, 0)); - } - // TODO: Print value as comment. Does dbuf_printf support long long even on - // MSVC? - dbuf_printf(oBuf, "\n"); + printIvalVal(oBuf, val, 8, + true); // TODO: Print value as comment. Does dbuf_printf + // support long long even on MSVC? break; default: wassertl(0, "Attempting to initialize integer of non-handled size."); @@ -679,14 +829,16 @@ void printIvalType(symbol *sym, sym_link *type, initList *ilist, /*-----------------------------------------------------------------*/ /* printIvalBitFields - generate initializer for bitfields */ +/* return the number of bytes written */ /*-----------------------------------------------------------------*/ -static void printIvalBitFields(symbol **sym, initList **ilist, - struct dbuf_s *oBuf) { +static unsigned int printIvalBitFields(symbol **sym, initList **ilist, + struct dbuf_s *oBuf) { symbol *lsym = *sym; initList *lilist = *ilist; unsigned long ival = 0; unsigned size = 0; unsigned bit_start = 0; + unsigned long int bytes_written = 0; while (lsym && IS_BITFIELD(lsym->type)) { unsigned bit_length = SPEC_BLEN(lsym->etype); @@ -696,7 +848,7 @@ static void printIvalBitFields(symbol **sym, initList **ilist, break; } else if (!SPEC_BUNNAMED(lsym->etype)) { /* not an unnamed bit-field structure member */ - value *val = list2val(lilist); + value *val = list2val(lilist, TRUE); if (val && val->etype && SPEC_SCLS(val->etype) != S_LITERAL) { werrorfl(lilist->filename, lilist->lineno, E_CONST_EXPECTED); @@ -726,23 +878,34 @@ static void printIvalBitFields(symbol **sym, initList **ilist, } switch (size) { + case 0: + dbuf_tprintf(oBuf, "\n"); + break; + case 1: dbuf_tprintf(oBuf, "\t!db !constbyte\n", ival); + bytes_written++; break; case 2: dbuf_tprintf(oBuf, "\t!db !constbyte, !constbyte\n", (ival & 0xff), (ival >> 8) & 0xff); + bytes_written += 2; break; case 4: dbuf_tprintf(oBuf, "\t!db !constbyte, !constbyte, !constbyte, !constbyte\n", (ival & 0xff), (ival >> 8) & 0xff, (ival >> 16) & 0xff, (ival >> 24) & 0xff); + bytes_written += 4; break; + default: + wassert(0); } *sym = lsym; *ilist = lilist; + + return (bytes_written); } /*-----------------------------------------------------------------*/ @@ -752,6 +915,7 @@ static void printIvalStruct(symbol *sym, sym_link *type, initList *ilist, struct dbuf_s *oBuf) { symbol *sflds; initList *iloop = NULL; + unsigned int skip_holes = 0; sflds = SPEC_STRUCT(type)->fields; @@ -784,6 +948,8 @@ static void printIvalStruct(symbol *sym, sym_link *type, initList *ilist, } while (iloop && iloop->type == INIT_HOLE); } else { while (sflds) { + unsigned int oldoffset = sflds->offset; + if (IS_BITFIELD(sflds->type)) printIvalBitFields(&sflds, &iloop, oBuf); else { @@ -791,6 +957,17 @@ static void printIvalStruct(symbol *sym, sym_link *type, initList *ilist, sflds = sflds->next; iloop = iloop ? iloop->next : NULL; } + + // Handle members from anonymous unions. Just a hack to fix bug #2643. + while (sflds && sflds->offset == oldoffset) { + sflds = sflds->next; + skip_holes++; + } + } + + while (skip_holes && iloop && iloop->type == INIT_HOLE) { + skip_holes--; + iloop = iloop ? iloop->next : NULL; } } @@ -805,10 +982,12 @@ static void printIvalStruct(symbol *sym, sym_link *type, initList *ilist, int printIvalChar(symbol *sym, sym_link *type, initList *ilist, struct dbuf_s *oBuf, const char *s, bool check) { value *val; - unsigned int size = DCL_ELEM(type); + size_t size = DCL_ELEM(type); + char *p; + size_t asz; if (!s) { - val = list2val(ilist); + val = list2val(ilist, TRUE); /* if the value is a character string */ if (IS_ARRAY(val->type) && IS_CHAR(val->etype)) { if (!size) { @@ -825,7 +1004,13 @@ int printIvalChar(symbol *sym, sym_link *type, initList *ilist, werror(W_EXCESS_INITIALIZERS, "array of chars", sym->name, sym->lineDef); - printChar(oBuf, SPEC_CVAL(val->etype).v_char, size); + if (size > (asz = DCL_ELEM(val->type)) && !!(p = malloc(size))) { + memcpy(p, SPEC_CVAL(val->etype).v_char, asz); + memset(p + asz, 0x00, size - asz); + printChar(oBuf, p, size); + free(p); + } else + printChar(oBuf, SPEC_CVAL(val->etype).v_char, size); return 1; } else @@ -835,6 +1020,113 @@ int printIvalChar(symbol *sym, sym_link *type, initList *ilist, return 1; } +static size_t strLen16(const TYPE_TARGET_UINT *s) { + size_t l = 0; + while (*s++) + l++; + + return l; +} + +/*-----------------------------------------------------------------*/ +/* printIvalChar16 - generates initital value for character array */ +/*-----------------------------------------------------------------*/ +int printIvalChar16(symbol *sym, sym_link *type, initList *ilist, + struct dbuf_s *oBuf, const TYPE_TARGET_UINT *s, + bool check) { + value *val; + size_t size = DCL_ELEM(type); + TYPE_TARGET_UINT *p; + size_t asz; + + if (!s) { + val = list2val(ilist, TRUE); + /* if the value is a character string */ + if (IS_ARRAY(val->type) && IS_INT(val->etype) && IS_UNSIGNED(val->etype) && + !IS_LONG(val->etype)) { + if (!size) { + /* we have not been given a size, but now we know it */ + size = strLen16(SPEC_CVAL(val->etype).v_char16) + 1; + /* but first check, if it's a flexible array */ + if (sym && IS_STRUCT(sym->type)) + sym->flexArrayLength = size; + else + DCL_ELEM(type) = size; + } + + if (check && DCL_ELEM(val->type) > size) + werror(W_EXCESS_INITIALIZERS, "array of chars", sym->name, + sym->lineDef); + + if (size > (asz = DCL_ELEM(val->type)) && !!(p = malloc(size * 2))) { + memcpy(p, SPEC_CVAL(val->etype).v_char16, asz * 2); + memset(p + asz, 0x00, size * 2 - asz * 2); + printChar16(oBuf, p, size); + free(p); + } else + printChar16(oBuf, SPEC_CVAL(val->etype).v_char16, size); + + return 1; + } else + return 0; + } else + printChar16(oBuf, s, strLen16(s) + 1); + return 1; +} + +static size_t strLen32(const TYPE_TARGET_ULONG *s) { + size_t l = 0; + while (*s++) + l++; + return l; +} + +/*-----------------------------------------------------------------*/ +/* printIvalChar32 - generates initital value for character array */ +/*-----------------------------------------------------------------*/ +int printIvalChar32(symbol *sym, sym_link *type, initList *ilist, + struct dbuf_s *oBuf, const TYPE_TARGET_ULONG *s, + bool check) { + value *val; + size_t size = DCL_ELEM(type); + TYPE_TARGET_ULONG *p; + size_t asz; + + if (!s) { + val = list2val(ilist, TRUE); + /* if the value is a character string */ + if (IS_ARRAY(val->type) && IS_INT(val->etype) && IS_UNSIGNED(val->etype) && + IS_LONG(val->etype)) { + if (!size) { + /* we have not been given a size, but now we know it */ + size = strLen32(SPEC_CVAL(val->etype).v_char32) + 1; + /* but first check, if it's a flexible array */ + if (sym && IS_STRUCT(sym->type)) + sym->flexArrayLength = size; + else + DCL_ELEM(type) = size; + } + + if (check && DCL_ELEM(val->type) > size) + werror(W_EXCESS_INITIALIZERS, "array of chars", sym->name, + sym->lineDef); + + if (size > (asz = DCL_ELEM(val->type)) && !!(p = malloc(size * 4))) { + memcpy(p, SPEC_CVAL(val->etype).v_char32, asz * 4); + memset(p + asz, 0x00, size * 4 - asz * 4); + printChar32(oBuf, p, size); + free(p); + } else + printChar32(oBuf, SPEC_CVAL(val->etype).v_char32, size); + + return 1; + } else + return 0; + } else + printChar32(oBuf, s, strLen32(s) + 1); + return 1; +} + /*-----------------------------------------------------------------*/ /* printIvalArray - generates code for array initialization */ /*-----------------------------------------------------------------*/ @@ -848,8 +1140,10 @@ void printIvalArray(symbol *sym, sym_link *type, initList *ilist, /* take care of the special case */ /* array of characters can be init */ /* by a string */ - if (IS_CHAR(type->next) && ilist->type == INIT_NODE) { - val = list2val(ilist); + /* char *p = "abc"; */ + if ((IS_CHAR(type->next) || IS_INT(type->next)) && + ilist->type == INIT_NODE) { + val = list2val(ilist, TRUE); if (!val) { werrorfl(ilist->filename, ilist->lineno, E_INIT_STRUCT, sym->name); return; @@ -858,11 +1152,52 @@ void printIvalArray(symbol *sym, sym_link *type, initList *ilist, werrorfl(ilist->filename, ilist->lineno, E_CONST_EXPECTED); return; } - if (printIvalChar(sym, type, - (ilist->type == INIT_DEEP ? ilist->init.deep : ilist), - oBuf, SPEC_CVAL(sym->etype).v_char, check)) + if (IS_CHAR(type->next) && + printIvalChar(sym, type, ilist, oBuf, SPEC_CVAL(sym->etype).v_char, + check)) + return; + if (IS_INT(type->next) && IS_UNSIGNED(type->next)) { + if (!IS_LONG(type->next) && + printIvalChar16(sym, type, ilist, oBuf, + SPEC_CVAL(sym->etype).v_char16, check)) + return; + if (IS_LONG(type->next) && + printIvalChar32(sym, type, ilist, oBuf, + SPEC_CVAL(sym->etype).v_char32, check)) + return; + } + } + /* char *p = {"abc"}; */ + if ((IS_CHAR(type->next) || IS_INT(type->next)) && + ilist->type == INIT_DEEP && ilist->init.deep && + ilist->init.deep->type == INIT_NODE) { + val = list2val(ilist->init.deep, TRUE); + if (!val) { + werrorfl(ilist->init.deep->filename, ilist->init.deep->lineno, + E_INIT_STRUCT, sym->name); + return; + } + if (!IS_LITERAL(val->etype)) { + werrorfl(ilist->init.deep->filename, ilist->init.deep->lineno, + E_CONST_EXPECTED); return; + } + if (IS_CHAR(type->next) && + printIvalChar(sym, type, ilist->init.deep, oBuf, + SPEC_CVAL(sym->etype).v_char, check)) + return; + if (IS_INT(type->next) && IS_UNSIGNED(type->next)) { + if (!IS_LONG(type->next) && + printIvalChar16(sym, type, ilist->init.deep, oBuf, + SPEC_CVAL(sym->etype).v_char16, check)) + return; + if (IS_LONG(type->next) && + printIvalChar32(sym, type, ilist->init.deep, oBuf, + SPEC_CVAL(sym->etype).v_char32, check)) + return; + } } + /* not the special case */ if (ilist->type != INIT_DEEP) { werrorfl(ilist->filename, ilist->lineno, E_INIT_STRUCT, sym->name); @@ -908,9 +1243,9 @@ void printIvalFuncPtr(sym_link *type, initList *ilist, struct dbuf_s *oBuf) { int size; if (ilist) - val = list2val(ilist); + val = list2val(ilist, TRUE); else - val = valCastLiteral(type, 0.0); + val = valCastLiteral(type, 0.0, 0); if (!val) { // an error has been thrown already @@ -937,17 +1272,17 @@ void printIvalFuncPtr(sym_link *type, initList *ilist, struct dbuf_s *oBuf) { size = getSize(type); - if (size == FPTRSIZE) { + if (size == FUNCPTRSIZE) { if (port->use_dw_for_init) { dbuf_tprintf(oBuf, "\t!dws\n", name); } else { printPointerType(oBuf, name); } - } else if (size == GPTRSIZE) { + } else if (size == BFUNCPTRSIZE) { _printPointerType(oBuf, name, size); dbuf_printf(oBuf, "\n"); } else { - assert(0); + wassertl(0, "Invalid function pointer size."); } return; @@ -959,6 +1294,12 @@ void printIvalFuncPtr(sym_link *type, initList *ilist, struct dbuf_s *oBuf) { int printIvalCharPtr(symbol *sym, sym_link *type, value *val, struct dbuf_s *oBuf) { int size = 0; + char *p; + + if (val && !!(p = (char *)malloc(strlen(val->name) + 1))) { + strcpy(p, val->name); + addSet(&ccpStr, p); + } /* PENDING: this is _very_ mcs51 specific, including a magic number... @@ -970,12 +1311,11 @@ int printIvalCharPtr(symbol *sym, sym_link *type, value *val, if (size == 1) /* This appears to be Z80 specific?? */ { dbuf_tprintf(oBuf, "\t!dbs\n", val->name); - } else if (size == FPTRSIZE) { - if (port->use_dw_for_init) { + } else if (size == FARPTRSIZE) { + if (port->use_dw_for_init) dbuf_tprintf(oBuf, "\t!dws\n", val->name); - } else { + else printPointerType(oBuf, val->name); - } } else if (size == GPTRSIZE) { int type; if (IS_PTR(val->type)) { @@ -1009,7 +1349,7 @@ int printIvalCharPtr(symbol *sym, sym_link *type, value *val, aopLiteral(val, 0)); break; case 3: - if (IS_GENPTR(type) && GPTRSIZE > FPTRSIZE && floatFromVal(val) != 0) { + if (IS_GENPTR(type) && GPTRSIZE > FARPTRSIZE && floatFromVal(val) != 0) { if (!IS_PTR(val->type) && !IS_FUNC(val->type)) { // non-zero mcs51 generic pointer werrorfl(sym->fileDef, sym->lineDef, W_LITERAL_GENERIC); @@ -1031,7 +1371,7 @@ int printIvalCharPtr(symbol *sym, sym_link *type, value *val, } break; case 4: - if (IS_GENPTR(type) && GPTRSIZE > FPTRSIZE && floatFromVal(val) != 0) { + if (IS_GENPTR(type) && GPTRSIZE > FARPTRSIZE && floatFromVal(val) != 0) { if (!IS_PTR(val->type) && !IS_FUNC(val->type)) { // non-zero ds390 generic pointer werrorfl(sym->fileDef, sym->lineDef, W_LITERAL_GENERIC); @@ -1098,11 +1438,11 @@ void printIvalPtr(symbol *sym, sym_link *type, initList *ilist, return; } - if (!(val = initPointer(ilist, type))) + if (!(val = initPointer(ilist, type, 1))) return; /* if character pointer */ - if (IS_CHAR(type->next)) + if (IS_CHAR(type->next) || IS_INT(type->next) && IS_UNSIGNED(type->next)) if (printIvalCharPtr(sym, type, val, oBuf)) return; @@ -1130,7 +1470,7 @@ void printIvalPtr(symbol *sym, sym_link *type, initList *ilist, dbuf_tprintf(oBuf, "\t.byte %s,%s\n", aopLiteral(val, 1), aopLiteral(val, 0)); break; - case 3: // how about '390?? + case 3: dbuf_printf(oBuf, "; generic printIvalPtr\n"); if (port->little_endian) dbuf_printf(oBuf, "\t.byte %s,%s", aopLiteral(val, 0), @@ -1146,6 +1486,13 @@ void printIvalPtr(symbol *sym, sym_link *type, initList *ilist, pointerTypeToGPByte(DCL_TYPE(val->type), val->name, sym->name)); else dbuf_printf(oBuf, ",%s\n", aopLiteral(val, 2)); + break; + case 4: + wassertl(0, "Printing pointer of invalid size"); + break; + default: + wassertl(0, "Printing pointer of invalid size"); + break; } return; } @@ -1155,7 +1502,7 @@ void printIvalPtr(symbol *sym, sym_link *type, initList *ilist, if (size == 1) /* Z80 specific?? */ { dbuf_tprintf(oBuf, "\t!dbs\n", val->name); - } else if (size == FPTRSIZE) { + } else if (size == FARPTRSIZE) { if (port->use_dw_for_init) dbuf_tprintf(oBuf, "\t!dws\n", val->name); else @@ -1165,6 +1512,7 @@ void printIvalPtr(symbol *sym, sym_link *type, initList *ilist, (IS_PTR(val->type) ? DCL_TYPE(val->type) : PTR_TYPE(SPEC_OCLS(val->etype)))); } + return; } @@ -1256,15 +1604,33 @@ void printIval(symbol *sym, sym_link *type, initList *ilist, /*-----------------------------------------------------------------*/ void emitStaticSeg(memmap *map, struct dbuf_s *oBuf) { symbol *sym; + set *tmpSet = NULL; /* fprintf(out, "\t.area\t%s\n", map->sname); */ + /* eliminate redundant __str_%d (generated in stringToSymbol(), SDCCast.c) */ + for (sym = setFirstItem(map->syms); sym; sym = setNextItem(map->syms)) + addSet(&tmpSet, sym); + /* for all variables in this segment do */ for (sym = setFirstItem(map->syms); sym; sym = setNextItem(map->syms)) { /* if it is "extern" then do nothing */ if (IS_EXTERN(sym->etype) && !sym->ival) continue; + /* eliminate redundant __str_%d (generated in stringToSymbol(), SDCCast.c) + */ + if (!isinSet(tmpSet, sym)) { + const char *p; + if (!ccpStr) + continue; + for (p = setFirstItem(ccpStr); p; p = setNextItem(ccpStr)) + if (strcmp(p, sym->name) == 0) + break; + if (!p) + continue; + } + /* if it is not static add it to the public table */ if (!IS_STATIC(sym->etype)) { addSetHead(&publics, sym); @@ -1294,22 +1660,49 @@ void emitStaticSeg(memmap *map, struct dbuf_s *oBuf) { WE don't need it anymore */ if (IS_ARRAY(sym->type) && IS_CHAR(sym->type->next) && IS_AST_SYM_VALUE(list2expr(sym->ival)) && - list2val(sym->ival)->sym->isstrlit) { - freeStringSymbol(list2val(sym->ival)->sym); + list2val(sym->ival, TRUE)->sym->isstrlit) { + freeStringSymbol(list2val(sym->ival, TRUE)->sym); } } else { /* allocate space */ dbuf_printf(oBuf, "%s:\n", sym->rname); /* special case for character strings */ - if (IS_ARRAY(sym->type) && IS_CHAR(sym->type->next) && - SPEC_CVAL(sym->etype).v_char) { - printChar(oBuf, SPEC_CVAL(sym->etype).v_char, size); + if (IS_ARRAY(sym->type) && + (IS_CHAR(sym->type->next) && SPEC_CVAL(sym->etype).v_char || + IS_INT(sym->type->next) && !IS_LONG(sym->type->next) && + SPEC_CVAL(sym->etype).v_char16 || + IS_INT(sym->type->next) && IS_LONG(sym->type->next) && + SPEC_CVAL(sym->etype).v_char32)) { + if (options.const_seg) + dbuf_tprintf(&code->oBuf, "\t!area\n", options.const_seg); + dbuf_printf(oBuf, "%s:\n", sym->rname); + if (IS_CHAR(sym->type->next)) + printChar(oBuf, SPEC_CVAL(sym->etype).v_char, size); + else if (IS_INT(sym->type->next) && !IS_LONG(sym->type->next)) + printChar16(oBuf, SPEC_CVAL(sym->etype).v_char16, size / 2); + else if (IS_INT(sym->type->next) && IS_LONG(sym->type->next)) + printChar32(oBuf, SPEC_CVAL(sym->etype).v_char32, size / 4); + else + wassert(0); + if (options.const_seg) + dbuf_tprintf(oBuf, "\t!areacode\n", options.code_seg); } else { + dbuf_printf(oBuf, "%s:\n", sym->rname); dbuf_tprintf(oBuf, "\t!ds\n", (unsigned int)size & 0xffff); } } } } + + if (tmpSet) + deleteSet(&tmpSet); + if (ccpStr) { + char *p; + for (p = setFirstItem(ccpStr); p; p = setNextItem(ccpStr)) + if (p) + free(p); + deleteSet(&ccpStr); + } } /*-----------------------------------------------------------------*/ @@ -1317,6 +1710,10 @@ void emitStaticSeg(memmap *map, struct dbuf_s *oBuf) { /*-----------------------------------------------------------------*/ void emitMaps(void) { namedspacemap *nm; + bool publicsfr = false; + /* Ideally, this should be true for all */ + /* ports but let's be conservative - EEP */ + inInitMode++; /* no special considerations for the following data, idata & bit & xdata */ @@ -1338,15 +1735,15 @@ void emitMaps(void) { if (port->genXINIT) { emitRegularMap(xidata, TRUE, TRUE); } - emitRegularMap(sfr, TRUE, FALSE); - emitRegularMap(sfrbit, TRUE, FALSE); + emitRegularMap(sfr, publicsfr, FALSE); + emitRegularMap(sfrbit, publicsfr, FALSE); emitRegularMap(home, TRUE, FALSE); emitRegularMap(code, TRUE, FALSE); - if (options.const_seg) { + if (options.const_seg) dbuf_tprintf(&code->oBuf, "\t!area\n", options.const_seg); - } emitStaticSeg(statsg, &code->oBuf); + if (port->genXINIT) { dbuf_tprintf(&code->oBuf, "\t!area\n", xinit->sname); emitStaticSeg(xinit, &code->oBuf); @@ -1411,12 +1808,9 @@ char *iComments2 = { /* initialComments - puts in some initial comments */ /*-----------------------------------------------------------------*/ void initialComments(FILE *afile) { - time_t t; - time(&t); fprintf(afile, "%s", iComments1); fprintf(afile, "; Version " KCC_VERSION_STR " (%s) (%s)\n", getBuildDate(), getBuildEnvironment()); - fprintf(afile, "; This file was generated %s", asctime(localtime(&t))); fprintf(afile, "%s", iComments2); } @@ -1430,8 +1824,14 @@ void printPublics(FILE *afile) { fprintf(afile, "; Public variables in this module\n"); fprintf(afile, "%s", iComments2); - for (sym = setFirstItem(publics); sym; sym = setNextItem(publics)) + for (sym = setFirstItem(publics); sym; sym = setNextItem(publics)) { + if (IFFUNC_BANKED(sym->type)) { + /* TODO: use template for bank symbol generation */ + sprintf(buffer, "b%s", sym->rname); + tfprintf(afile, "\t!global\n", buffer); + } tfprintf(afile, "\t!global\n", sym->rname); + } } /*-----------------------------------------------------------------*/ @@ -1548,7 +1948,7 @@ void glue(void) { } if (!(asmFile = fopen(dbuf_c_str(&asmFileName), "w"))) { - werror(E_FILE_OPEN_ERR, dbuf_c_str(&asmFileName)); + werror(E_OUTPUT_FILE_OPEN_ERR, dbuf_c_str(&asmFileName), strerror(errno)); dbuf_destroy(&asmFileName); exit(EXIT_FAILURE); } @@ -1719,7 +2119,7 @@ void glue(void) { } dbuf_write_and_destroy(&statsg->oBuf, asmFile); - /* STM8 note: there are no such instructions supported. + /* STM8 / PDK14 note: there are no such instructions supported. Also, we don't need this logic as well. */ if (port->general.glue_up_main && mainf && IFFUNC_HASBODY(mainf->type)) { /* This code is generated in the post-static area. @@ -1764,3 +2164,30 @@ void glue(void) { } fclose(asmFile); } + +/* will return 1 if the string is a part + of a target specific keyword */ +int isTargetKeyword(const char *s) { + int i; + + if (port->keywords == NULL) + return 0; + + if (s[0] == '_' && s[1] == '_') { + /* Keywords in the port's array have either 0 or 1 underscore, */ + /* so skip over the appropriate number of chars when comparing */ + for (i = 0; port->keywords[i]; i++) { + if (port->keywords[i][0] == '_' && strcmp(port->keywords[i], s + 1) == 0) + return 1; + else if (strcmp(port->keywords[i], s + 2) == 0) + return 1; + } + } else { + for (i = 0; port->keywords[i]; i++) { + if (strcmp(port->keywords[i], s) == 0) + return 1; + } + } + + return 0; +} diff --git a/src/SDCCglue.h b/src/SDCCglue.h index 532f4cf9f..0d1955132 100644 --- a/src/SDCCglue.h +++ b/src/SDCCglue.h @@ -28,15 +28,17 @@ #define SDCCGLUE_H 1 void glue(void); -/* drdani Jan 30 2000 - This is needed in gen.c of z80 port */ -char *aopLiteral(value *, int); +const char *aopLiteral(value *, int offset); +const char *aopLiteralLong(value *val, int offset, int size); void flushStatics(void); int printIvalCharPtr(symbol *, sym_link *, value *, struct dbuf_s *); extern symbol *interrupts[]; extern set *publics; +extern set *strSym; int pointerTypeToGPByte(const int p_type, const char *iname, const char *oname); +int isTargetKeyword(const char *s); + #endif diff --git a/src/SDCCicode.c b/src/SDCCicode.c index 7ee17e6a8..f34d6f1b4 100644 --- a/src/SDCCicode.c +++ b/src/SDCCicode.c @@ -38,8 +38,9 @@ int iCodeKey = 0; char *filename; /* current file name */ int lineno = 1; /* current line number */ int block; -int scopeLevel; +long scopeLevel; int seqPoint; +int inCriticalPair = 0; symbol *returnLabel; /* function return label */ symbol *entryLabel; /* function entry label */ @@ -109,6 +110,7 @@ iCodeTable codeTable[] = { {LEFT_OP, "<<", picGeneric, NULL}, {RIGHT_OP, ">>", picGeneric, NULL}, {GET_VALUE_AT_ADDRESS, "@", picGetValueAtAddr, NULL}, + {SET_VALUE_AT_ADDRESS, "@", picSetValueAtAddr, NULL}, {ADDRESS_OF, "&", picAddrOf, NULL}, {CAST, "<>", picCast, NULL}, {'=', ":=", picAssign, NULL}, @@ -161,6 +163,10 @@ int dbuf_printOperand(operand *op, struct dbuf_s *dbuf) { else if (IS_FIXED16X16(opetype)) dbuf_printf(dbuf, "%g {", doubleFromFixed16x16(SPEC_CVAL(opetype).v_fixed16x16)); + else if (IS_LONGLONG(opetype)) + dbuf_printf( + dbuf, "0x%llx {", + (unsigned long long)SPEC_CVAL(OP_VALUE(op)->etype).v_ulonglong); else dbuf_printf(dbuf, "0x%x {", (unsigned int)ulFromVal(OP_VALUE(op))); dbuf_printTypeChain(operandType(op), dbuf); @@ -457,8 +463,8 @@ int piCode(void *item, FILE *of) { of = stdout; icTab = getTableEntry(ic->op); - fprintf(of, "%s(%d:%d:%d:%d:%d)\t", ic->filename, ic->lineno, ic->seq, - ic->key, ic->depth, ic->supportRtn); + fprintf(of, "%s(%d:%d:%d:%d:%d:%d)\t", ic->filename, ic->lineno, ic->seq, + ic->key, ic->depth, ic->supportRtn, ic->block); dbuf_init(&dbuf, 1024); icTab->iCodePrint(&dbuf, ic, icTab->printName); dbuf_write_and_destroy(&dbuf, of); @@ -480,8 +486,8 @@ void printiCChain(iCode *icChain, FILE *of) { if ((icTab = getTableEntry(loop->op))) { struct dbuf_s dbuf; - fprintf(of, "%s(l%d:s%d:k%d:d%d:s%d)\t", loop->filename, loop->lineno, - loop->seq, loop->key, loop->depth, loop->supportRtn); + fprintf(of, "%s(l%d:s%d:k%d:d%d:s%d:b%d)\t", loop->filename, loop->lineno, + loop->seq, loop->key, loop->depth, loop->supportRtn, loop->block); dbuf_init(&dbuf, 1024); icTab->iCodePrint(&dbuf, loop, icTab->printName); @@ -522,6 +528,10 @@ iCode *newiCode(int op, operand *left, operand *right) { IC_LEFT(ic) = left; IC_RIGHT(ic) = right; + // Err on the save side for now, settign this to false later is up to later + // analysis. + ic->localEscapeAlive = true; + return ic; } @@ -968,6 +978,63 @@ bool isOperandOnStack(operand *op) { return FALSE; } +/*-------------------------------------------------------------------*/ +/* detachiCodeOperand - remove a specific operand position (left, */ +/* right, result, etc) from an iCode and update */ +/* the uses & defs as appropriate. */ +/*-------------------------------------------------------------------*/ +operand *detachiCodeOperand(operand **opp, iCode *ic) { + operand *op = *opp; + + if (IS_SYMOP(op)) { + if ((ic->op == IFX) || (ic->op == JUMPTABLE)) { + *opp = NULL; + bitVectUnSetBit(OP_USES(op), ic->key); + } else { + int uses = 0; + bool ispointerset = POINTER_SET(ic); + + if (!ispointerset && (opp == &IC_RESULT(ic))) + bitVectUnSetBit(OP_DEFS(op), ic->key); + *opp = NULL; + if (ispointerset && (op == IC_RESULT(ic))) + uses++; + if (op == IC_LEFT(ic)) + uses++; + if (op == IC_RIGHT(ic)) + uses++; + if (uses == 0) + bitVectUnSetBit(OP_USES(op), ic->key); + } + } else + *opp = NULL; + return op; +} + +/*-------------------------------------------------------------------*/ +/* attachiCodeOperand - insert an operand to a specific operand */ +/* position (left, right, result, etc) in an */ +/* iCode and update the uses & defs as */ +/* appropriate. Any previously existing operand */ +/* in that position will be detached first. */ +/*-------------------------------------------------------------------*/ +void attachiCodeOperand(operand *newop, operand **opp, iCode *ic) { + /* If there is already an operand here, detach it first */ + if (*opp) + detachiCodeOperand(opp, ic); + + /* Insert new operand */ + *opp = newop; + + /* Update defs/uses for new operand */ + if (IS_SYMOP(newop)) { + if (opp == &IC_RESULT(ic) && !POINTER_SET(ic)) + OP_DEFS(newop) = bitVectSetBit(OP_DEFS(newop), ic->key); + else + OP_USES(newop) = bitVectSetBit(OP_USES(newop), ic->key); + } +} + /*-----------------------------------------------------------------*/ /* isOclsExpensive - will return true if accesses to an output */ /* storage class are expensive */ @@ -1011,13 +1078,22 @@ int isiCodeInFunctionCall(iCode *ic) { return FALSE; } +/*-----------------------------------------------------------------*/ +/* operandLitValueUll - unsigned long long value of an operand */ +/*-----------------------------------------------------------------*/ +unsigned long long operandLitValueUll(const operand *op) { + assert(isOperandLiteral(op)); + + return ullFromVal(OP_VALUE_CONST(op)); +} + /*-----------------------------------------------------------------*/ /* operandLitValue - literal value of an operand */ /*-----------------------------------------------------------------*/ -double operandLitValue(operand *op) { +double operandLitValue(const operand *op) { assert(isOperandLiteral(op)); - return floatFromVal(OP_VALUE(op)); + return floatFromVal(OP_VALUE_CONST(op)); } extern bool regalloc_dry_run; @@ -1044,7 +1120,7 @@ iCode *getBuiltinParms(iCode *fic, int *pcount, operand **parms) { /* make sure this is a builtin function call */ assert(IS_SYMOP(IC_LEFT(ic))); ftype = operandType(IC_LEFT(ic)); - wassert(IFFUNC_ISBUILTIN(ftype)); + assert(IFFUNC_ISBUILTIN(ftype)); return ic; } @@ -1067,11 +1143,13 @@ operand *operandOperation(operand *left, operand *right, int op, switch (op) { case '+': retval = operandFromValue( - valCastLiteral(type, operandLitValue(left) + operandLitValue(right))); + valCastLiteral(type, operandLitValue(left) + operandLitValue(right), + operandLitValueUll(left) + operandLitValueUll(right))); break; case '-': retval = operandFromValue( - valCastLiteral(type, operandLitValue(left) - operandLitValue(right))); + valCastLiteral(type, operandLitValue(left) - operandLitValue(right), + operandLitValueUll(left) - operandLitValueUll(right))); break; case '*': /* @@ -1090,13 +1168,23 @@ operand *operandOperation(operand *left, operand *right, int op, /* if it is not a specifier then we can assume that */ /* it will be an unsigned long */ if (IS_INT(type) || !IS_SPEC(type)) { + /* long long is handled here, because it can overflow with double */ + if (IS_LONGLONG(type) || !IS_SPEC(type)) + /* signed and unsigned mul are the same, as long as the precision + of the result isn't bigger than the precision of the operands. */ + retval = operandFromValue(valCastLiteral( + type, operandLitValue(left) * operandLitValue(right), + operandLitValueUll(left) * operandLitValueUll(right))); /* long is handled here, because it can overflow with double */ - if (IS_LONG(type) || !IS_SPEC(type)) + else if (IS_LONG(type) || !IS_SPEC(type)) /* signed and unsigned mul are the same, as long as the precision of the result isn't bigger than the precision of the operands. */ retval = operandFromValue(valCastLiteral( - type, (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) * - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); + type, + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) * + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)), + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) * + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); else if (IS_UNSIGNED(type)) /* unsigned int */ { /* unsigned int is handled here in order to detect overflow */ @@ -1104,7 +1192,8 @@ operand *operandOperation(operand *left, operand *right, int op, (TYPE_TARGET_UINT)double2ul(operandLitValue(left)) * (TYPE_TARGET_UINT)double2ul(operandLitValue(right)); - retval = operandFromValue(valCastLiteral(type, (TYPE_TARGET_UINT)ul)); + retval = operandFromValue( + valCastLiteral(type, (TYPE_TARGET_UINT)ul, (TYPE_TARGET_UINT)ul)); if (ul != (TYPE_TARGET_UINT)ul) werror(W_INT_OVL); } else /* signed int */ @@ -1113,45 +1202,57 @@ operand *operandOperation(operand *left, operand *right, int op, TYPE_TARGET_LONG l = (TYPE_TARGET_INT)operandLitValue(left) * (TYPE_TARGET_INT)operandLitValue(right); - retval = operandFromValue(valCastLiteral(type, (TYPE_TARGET_INT)l)); + retval = operandFromValue( + valCastLiteral(type, (TYPE_TARGET_INT)l, (TYPE_TARGET_INT)l)); if (l != (TYPE_TARGET_INT)l) werror(W_INT_OVL); } } else /* all others go here: */ retval = operandFromValue( - valCastLiteral(type, operandLitValue(left) * operandLitValue(right))); + valCastLiteral(type, operandLitValue(left) * operandLitValue(right), + operandLitValueUll(left) * operandLitValueUll(right))); break; case '/': + if ((TYPE_TARGET_ULONG)double2ul(operandLitValue(right)) == 0 && + operandLitValueUll(right) == 0) { + werror(E_DIVIDE_BY_ZERO); + retval = right; + break; + } if (IS_UNSIGNED(type)) { - if ((TYPE_TARGET_ULONG)double2ul(operandLitValue(right)) == 0) { - werror(E_DIVIDE_BY_ZERO); - retval = right; - } SPEC_USIGN(let) = 1; SPEC_USIGN(ret) = 1; - retval = operandFromValue(valCastLiteral( - type, (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) / - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); - } else { - if (operandLitValue(right) == 0) { - werror(E_DIVIDE_BY_ZERO); - retval = right; - } + if (IS_LONGLONG(type)) + retval = operandFromValue(valCastLiteral( + type, 0.0, operandLitValueUll(left) / operandLitValueUll(right))); + else + retval = operandFromValue(valCastLiteral( + type, + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) / + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)), + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) / + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); + } else retval = operandFromValue( - valCastLiteral(type, operandLitValue(left) / operandLitValue(right))); - } + valCastLiteral(type, operandLitValue(left) / operandLitValue(right), + operandLitValueUll(left) / operandLitValueUll(right))); break; case '%': - if ((TYPE_TARGET_ULONG)double2ul(operandLitValue(right)) == 0) { + if ((TYPE_TARGET_ULONG)double2ul(operandLitValue(right)) == 0 && + operandLitValueUll(right) == 0) { werror(E_DIVIDE_BY_ZERO); retval = right; } else { - if (IS_UNSIGNED(type)) - retval = operandFromLit( - (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) % - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right))); - else + if (IS_UNSIGNED(type)) { + if (IS_LONGLONG(type)) + retval = operandFromValue(valCastLiteral( + type, 0.0, operandLitValueUll(left) % operandLitValueUll(right))); + else + retval = operandFromLit( + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) % + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right))); + } else retval = operandFromLit((TYPE_TARGET_LONG)operandLitValue(left) % (TYPE_TARGET_LONG)operandLitValue(right)); } @@ -1159,9 +1260,17 @@ operand *operandOperation(operand *left, operand *right, int op, case LEFT_OP: /* The number of left shifts is always unsigned. Signed doesn't make sense here. Shifting by a negative number is impossible. */ - retval = operandFromValue(valCastLiteral( - type, ((TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) - << (TYPE_TARGET_ULONG)double2ul(operandLitValue(right))))); + if (IS_LONGLONG(type)) + retval = operandFromValue(valCastLiteral( + type, (operandLitValueUll(left) << operandLitValueUll(right)), + (operandLitValueUll(left) << operandLitValueUll(right)))); + else + retval = operandFromValue(valCastLiteral( + type, + ((TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) + << (TYPE_TARGET_ULONG)double2ul(operandLitValue(right))), + ((TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) + << (TYPE_TARGET_ULONG)double2ul(operandLitValue(right))))); break; case RIGHT_OP: /* The number of right shifts is always unsigned. Signed doesn't make @@ -1211,18 +1320,27 @@ operand *operandOperation(operand *left, operand *right, int op, break; case BITWISEAND: retval = operandFromValue(valCastLiteral( - type, (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) & - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); + type, + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) & + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)), + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) & + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); break; case '|': retval = operandFromValue(valCastLiteral( - type, (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) | - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); + type, + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) | + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)), + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) | + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); break; case '^': retval = operandFromValue(valCastLiteral( - type, (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) ^ - (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); + type, + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) ^ + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)), + (TYPE_TARGET_ULONG)double2ul(operandLitValue(left)) ^ + (TYPE_TARGET_ULONG)double2ul(operandLitValue(right)))); break; case AND_OP: retval = operandFromLit(operandLitValue(left) && operandLitValue(right)); @@ -1269,12 +1387,14 @@ operand *operandOperation(operand *left, operand *right, int op, break; case UNARYMINUS: - retval = operandFromValue(valCastLiteral(type, -1 * operandLitValue(left))); + retval = operandFromValue(valCastLiteral( + type, -1 * operandLitValue(left), (-1ll) * operandLitValueUll(left))); break; case '~': retval = operandFromValue(valCastLiteral( - type, ~((TYPE_TARGET_ULONG)double2ul(operandLitValue(left))))); + type, ~((TYPE_TARGET_ULONG)double2ul(operandLitValue(left))), + ~((TYPE_TARGET_ULONGLONG)operandLitValueUll(left)))); break; case '!': @@ -1282,7 +1402,9 @@ operand *operandOperation(operand *left, operand *right, int op, break; case ADDRESS_OF: - retval = operandFromValue(valCastLiteral(type, operandLitValue(left))); + retval = operandFromValue( + valCastLiteral(type, operandLitValue(left), + (TYPE_TARGET_ULONGLONG)operandLitValueUll(left))); break; default: @@ -1319,8 +1441,9 @@ int isOperandEqual(const operand *left, const operand *right) { case VALUE: return ( compareType(left->svt.valOperand->type, right->svt.valOperand->type) && - (floatFromVal(left->svt.valOperand) == - floatFromVal(right->svt.valOperand))); + (!IS_FLOAT(getSpec(left->svt.valOperand->type)) + ? (operandLitValueUll(left) == operandLitValueUll(right)) + : (operandLitValue(left) == operandLitValue(right)))); case TYPE: if (compareType(left->svt.typeOperand, right->svt.typeOperand) == 1) return 1; @@ -1402,6 +1525,7 @@ operand *operandFromOperand(operand *op) { nop->isLiteral = op->isLiteral; nop->usesDefs = op->usesDefs; nop->isParm = op->isParm; + nop->isConstElimnated = op->isConstElimnated; switch (nop->type) { case SYMBOL: @@ -1462,20 +1586,21 @@ operand *operandFromSymbol(symbol *sym) { /* under the following conditions create a register equivalent for a local symbol */ if (sym->level && sym->etype && SPEC_OCLS(sym->etype) && - (IN_FARSPACE(SPEC_OCLS(sym->etype))) && options.stackAuto == 0) { + (IN_FARSPACE(SPEC_OCLS(sym->etype)) && + (!(options.model == MODEL_FLAT24))) && + options.stackAuto == 0) { ok = 0; } - if (!IS_AGGREGATE(sym->type) && /* not an aggregate */ - !IS_FUNC(sym->type) && /* not a function */ - !sym->_isparm && /* not a parameter */ - IS_AUTO(sym) && /* is a local auto variable */ - !sym->addrtaken && /* whose address has not been taken */ - !sym->reqv && /* does not already have a reg equivalence */ - !IS_VOLATILE(sym->etype) && /* not declared as volatile */ - !sym->islbl && /* not a label */ - !(getSize(sym->type) > 2) && /* will fit in regs */ - ok /* farspace check */ + if (!IS_AGGREGATE(sym->type) && /* not an aggregate */ + !IS_FUNC(sym->type) && /* not a function */ + !sym->_isparm && /* not a parameter */ + IS_AUTO(sym) && /* is a local auto variable */ + !sym->addrtaken && /* whose address has not been taken */ + !sym->reqv && /* does not already have a reg equivalence */ + !IS_VOLATILE(sym->type) && /* not declared as volatile */ + !sym->islbl && /* not a label */ + ok /* farspace check */ ) { /* we will use it after all optimizations and before liveRange calculation */ @@ -1632,7 +1757,7 @@ static unsigned int getArraySizePtr(operand *op) { if (IS_PTR(ltype)) { int size = getSize(ltype); - return ((IS_GENPTR(ltype) && GPTRSIZE > FPTRSIZE) ? (size - 1) : size); + return ((IS_GENPTR(ltype) && GPTRSIZE > FARPTRSIZE) ? (size - 1) : size); } if (IS_ARRAY(ltype)) { @@ -1641,23 +1766,23 @@ static unsigned int getArraySizePtr(operand *op) { case IPOINTER: case PPOINTER: case POINTER: - return (PTRSIZE); + return (NEARPTRSIZE); case EEPPOINTER: case FPOINTER: case CPOINTER: case FUNCTION: - return (FPTRSIZE); + return (FARPTRSIZE); case GPOINTER: - if (GPTRSIZE > FPTRSIZE) + if (GPTRSIZE > FARPTRSIZE) return (GPTRSIZE - 1); else - return (FPTRSIZE); + return (FARPTRSIZE); default: - return (FPTRSIZE); + return (FARPTRSIZE); } } - return (FPTRSIZE); + return (FARPTRSIZE); } /*-----------------------------------------------------------------*/ @@ -1711,7 +1836,7 @@ static sym_link *usualBinaryConversions(operand **op1, operand **op2, } /*-----------------------------------------------------------------*/ -/* geniCodeValueAtAddress - generate intermeditate code for value */ +/* geniCodeValueAtAddress - generate intermediate code for value */ /* at address */ /*-----------------------------------------------------------------*/ operand *geniCodeRValue(operand *op, bool force) { @@ -1720,8 +1845,9 @@ operand *geniCodeRValue(operand *op, bool force) { sym_link *etype = getSpec(type); /* if this is an array & already */ - /* an address then return this */ - if (IS_AGGREGATE(type) || (IS_PTR(type) && !force && !op->isaddr)) + /* a resolved address then return this */ + if ((IS_ARRAY(type) && !IS_FUNCPTR(type->next)) || IS_STRUCT(type) || + (IS_PTR(type) && !force && !op->isaddr)) return operandFromOperand(op); /* if this is not an address then must be */ @@ -1737,14 +1863,14 @@ operand *geniCodeRValue(operand *op, bool force) { } if (IS_SPEC(type) && IS_TRUE_SYMOP(op) && - (!(IN_FARSPACE(SPEC_OCLS(etype))))) { + (!(IN_FARSPACE(SPEC_OCLS(etype))) || (options.model == MODEL_FLAT24))) { op = operandFromOperand(op); op->isaddr = 0; return op; } ic = newiCode(GET_VALUE_AT_ADDRESS, op, operandFromLit(0)); - if (IS_PTR(type) && op->isaddr && force) + if ((IS_PTR(type) && op->isaddr && force) || IS_ARRAY(type)) type = type->next; type = copyLinkChain(type); @@ -1762,10 +1888,17 @@ operand *geniCodeRValue(operand *op, bool force) { /*-----------------------------------------------------------------*/ /* checkPtrQualifiers - check for lost pointer qualifers */ /*-----------------------------------------------------------------*/ -static void checkPtrQualifiers(sym_link *ltype, sym_link *rtype) { - if (IS_PTR(ltype) && IS_PTR(rtype) && !IS_FUNCPTR(ltype)) { +static void checkPtrQualifiers(sym_link *ltype, sym_link *rtype, + int warn_const) { + if (IS_PTR(ltype) && IS_PTR(rtype) && !IS_FUNCPTR(ltype) && warn_const) { if (!IS_CONSTANT(ltype->next) && IS_CONSTANT(rtype->next)) werror(W_TARGET_LOST_QUALIFIER, "const"); +#if 0 + // disabled because SDCC will make all union fields volatile + // but your ptr to it need not be + if (!IS_VOLATILE (ltype->next) && IS_VOLATILE (rtype->next)) + werror (W_TARGET_LOST_QUALIFIER, "volatile"); +#endif if (!IS_RESTRICT(ltype->next) && IS_RESTRICT(rtype->next)) werror(W_TARGET_LOST_QUALIFIER, "restrict"); } @@ -1791,16 +1924,21 @@ static operand *geniCodeCast(sym_link *type, operand *op, bool implicit) { } /* if the operand is already the desired type then do nothing */ - if (compareType(type, optype) == 1) + if (compareType(type, optype) == 1) { + if (IS_PTR(type) && IS_CONSTANT(opetype) && !IS_CONSTANT(getSpec(type))) + op->isConstElimnated = 1; return op; + } /* if this is a literal then just change the type & return */ if (IS_LITERAL(opetype) && op->type == VALUE && !IS_PTR(type) && !IS_PTR(optype)) { - return operandFromValue(valCastLiteral(type, operandLitValue(op))); + return operandFromValue( + valCastLiteral(type, operandLitValue(op), operandLitValueUll(op))); } - checkPtrCast(type, optype, implicit); + checkPtrCast(type, optype, implicit, + IS_LITERAL(opetype) && !operandLitValue(op)); ic = newiCode(CAST, operandFromLink(type), geniCodeRValue(op, FALSE)); IC_RESULT(ic) = newiTempOperand(type, 0); @@ -1863,29 +2001,17 @@ static operand *geniCodeMultiply(operand *left, operand *right, /* code generated for 1 byte * 1 byte literal = 2 bytes result is more efficient in most cases than 2 bytes result = 2 bytes << literal if port has 1 byte muldiv */ - if ((p2 > 0) && !IS_FLOAT(letype) && !IS_FIXED(letype) && - !((resultType == RESULT_TYPE_INT) && - (getSize(resType) != getSize(ltype)) && (port->support.muldiv == 1))) { - if ((resultType == RESULT_TYPE_INT) && - (getSize(resType) != getSize(ltype))) { - /* LEFT_OP need same size for left and result, */ - left = geniCodeCast(resType, left, TRUE); - ltype = operandType(left); - } - ic = newiCode(LEFT_OP, left, operandFromLit(p2)); /* left shift */ + /* if the size left or right > 1 then support routine */ + if (getSize(ltype) > 1 || getSize(rtype) > 1) { + if (IS_LITERAL(retype)) + ic = newiCode( + '*', right, + left); /* multiplication by support routine with one literal */ + else + ic = newiCode('*', left, right); /* multiplication by support routine */ + ic->supportRtn = 1; } else { - /* if the size left or right > 1 then support routine */ - if (getSize(ltype) > 1 || getSize(rtype) > 1) { - if (IS_LITERAL(retype)) - ic = newiCode( - '*', right, - left); /* multiplication by support routine with one literal */ - else - ic = newiCode('*', left, right); /* multiplication by support routine */ - ic->supportRtn = 1; - } else { - ic = newiCode('*', left, right); /* normal multiplication */ - } + ic = newiCode('*', left, right); /* normal multiplication */ } IC_RESULT(ic) = newiTempOperand(resType, 1); @@ -1902,7 +2028,7 @@ operand *geniCodeRightShift(operand *left, operand *right); /* geniCodeDivision - gen intermediate code for division */ /*-----------------------------------------------------------------*/ static operand *geniCodeDivision(operand *left, operand *right, - RESULT_TYPE resultType) { + RESULT_TYPE resultType, bool ptrdiffdiv) { iCode *ic; int p2 = 0; sym_link *resType = usualBinaryConversions(&left, &right, resultType, '/'); @@ -1911,12 +2037,23 @@ static operand *geniCodeDivision(operand *left, operand *right, sym_link *ltype = operandType(left); sym_link *letype = getSpec(ltype); + /* if the right is a literal & power of 2 and left is unsigned then + make it a right shift. + For pointer division, there can be no remainder, so we can make + it a right shift, too. */ + + if (IS_LITERAL(retype) && + (!IS_FLOAT(letype) && !IS_FIXED(letype) && IS_UNSIGNED(letype) || + ptrdiffdiv) && + ((p2 = powof2((TYPE_TARGET_ULONG)ulFromVal(OP_VALUE(right)))) > 0)) { + ic = newiCode(RIGHT_OP, left, operandFromLit(p2)); /* right shift */ + } /* if the right is a literal & power of 2 and left is signed then make it a conditional addition followed by right shift */ - if (IS_LITERAL(retype) && !IS_FLOAT(letype) && !IS_FIXED(letype) && - !IS_UNSIGNED(letype) && - ((p2 = powof2((TYPE_TARGET_ULONG)ulFromVal(OP_VALUE(right)))) > 0)) { + else if (IS_LITERAL(retype) && !IS_FLOAT(letype) && !IS_FIXED(letype) && + !IS_UNSIGNED(letype) && + ((p2 = powof2((TYPE_TARGET_ULONG)ulFromVal(OP_VALUE(right)))) > 0)) { operand *tmp; symbol *label = newiTempLabel(NULL); @@ -1933,14 +2070,8 @@ static operand *geniCodeDivision(operand *left, operand *right, return (geniCodeCast(resType, geniCodeRightShift(tmp, operandFromLit(p2)), TRUE)); } - /* if the right is a literal & power of 2 - and left is unsigned then make it a - right shift */ - else if (IS_LITERAL(retype) && !IS_FLOAT(letype) && !IS_FIXED(letype) && - IS_UNSIGNED(letype) && - ((p2 = powof2((TYPE_TARGET_ULONG)ulFromVal(OP_VALUE(right)))) > 0)) { - ic = newiCode(RIGHT_OP, left, operandFromLit(p2)); /* right shift */ - } else { + + else { ic = newiCode('/', left, right); /* normal division */ /* if the size left or right > 1 then support routine */ if (getSize(ltype) > 1 || getSize(rtype) > 1) @@ -2003,8 +2134,8 @@ operand *geniCodePtrPtrSubtract(operand *left, operand *right) { return result; } - // should we really do this? is this ANSI? - return geniCodeDivision(result, operandFromLit(getSize(ltype->next)), FALSE); + return geniCodeDivision(result, operandFromLit(getSize(ltype->next)), FALSE, + true); } /*-----------------------------------------------------------------*/ @@ -2077,14 +2208,16 @@ static operand *geniCodeAdd(operand *left, operand *right, /* if left is a pointer then size */ if (IS_PTR(ltype) || IS_ARRAY(ltype)) { + unsigned int ptrSize; isarray = left->isaddr; nBytes = getSize(ltype->next); - if (nBytes == 0) + ptrSize = getArraySizePtr(left); // works for both arrays and pointers + + if (nBytes == 0 && !IS_VOID(ltype->next)) werror(E_UNKNOWN_SIZE, IS_SYMOP(left) ? OP_SYMBOL(left)->name : ""); // there is no need to multiply with 1 if (nBytes != 1) { - unsigned int ptrSize = getArraySizePtr(left); size = operandFromLit(nBytes); SPEC_USIGN(getSpec(operandType(size))) = 1; indexUnsigned = IS_UNSIGNED(getSpec(operandType(right))); @@ -2101,6 +2234,24 @@ static operand *geniCodeAdd(operand *left, operand *right, if (indexUnsigned) SPEC_USIGN(getSpec(operandType(right))) = 1; } + + if (ptrSize > getSize(rtype) && !IS_UNSIGNED(retype)) { + sym_link *type = 0; + + switch (ptrSize) { + case 2: + type = newIntLink(); + break; + case 3: + case 4: + type = newLongLink(); + break; + default: + wassert(0); + } + right = geniCodeCast(type, right, TRUE); + } + resType = copyLinkChain(ltype); } else { // make them the same size resType = usualBinaryConversions(&left, &right, resultType, '+'); @@ -2108,8 +2259,12 @@ static operand *geniCodeAdd(operand *left, operand *right, /* if they are both literals then we know */ if (IS_LITERAL(letype) && IS_LITERAL(retype) && left->isLiteral && - right->isLiteral) - return operandFromValue(valPlus(valFromType(ltype), valFromType(rtype))); + right->isLiteral) { + value *scaledRight = valFromType(rtype); + if (IS_PTR(ltype)) + scaledRight = valMult(scaledRight, valueFromLit(getSize(ltype->next))); + return operandFromValue(valPlus(valFromType(ltype), scaledRight)); + } ic = newiCode('+', left, right); @@ -2222,7 +2377,8 @@ static operand *geniCodeArray(operand *left, operand *right, int lvl) { ic = newiCode('+', left, right); IC_RESULT(ic) = newiTempOperand( - (IS_PTR(ltype) && !IS_AGGREGATE(ltype->next) && !IS_PTR(ltype->next)) + ((IS_PTR(ltype) && !IS_AGGREGATE(ltype->next) && !IS_PTR(ltype->next)) || + (IS_ARRAY(ltype) && IS_FUNCPTR(ltype->next))) ? ltype : ltype->next, 0); @@ -2248,7 +2404,8 @@ operand *geniCodeStruct(operand *left, operand *right, bool islval) { wassert(IS_SYMOP(right)); - wassert(IS_STRUCT(type) || (IS_PTR(type) && IS_STRUCT(type->next))); + wassert(IS_STRUCT(type) || + ((IS_PTR(type) || IS_ARRAY(type)) && IS_STRUCT(type->next))); /* add the offset */ ic = newiCode('+', left, operandFromLit(element->offset)); @@ -2269,7 +2426,7 @@ operand *geniCodeStruct(operand *left, operand *right, bool islval) { setOperandType(IC_RESULT(ic), aggrToPtr(operandType(IC_RESULT(ic)), TRUE)); } else { SPEC_CONST(retype) |= SPEC_CONST(etype); - SPEC_VOLATILE(retype) |= SPEC_VOLATILE(etype); + /*Do not preserve volatile */ SPEC_RESTRICT(retype) |= SPEC_RESTRICT(etype); } @@ -2293,6 +2450,7 @@ operand *geniCodePostInc(operand *op) { : op); sym_link *rvtype = operandType(rv); int size = 0; + operand *srcOp = rv; /* if this is not an address we have trouble */ if (!op->isaddr) { @@ -2308,17 +2466,23 @@ operand *geniCodePostInc(operand *op) { geniCodeAssign(rOp, rv, 0, 0); + /* If rv is volatile, we can only read it once, and we've just */ + /* done that, so use the copy in rOp instead to avoid reading */ + /* it again. */ + if (isOperandVolatile(rv, FALSE)) + srcOp = rOp; + size = (IS_PTR(rvtype) ? getSize(rvtype->next) : 1); if (size == 0) werror(W_SIZEOF_VOID); if (IS_FLOAT(rvtype)) - ic = newiCode('+', rv, operandFromValue(constFloatVal("1.0"))); + ic = newiCode('+', srcOp, operandFromValue(constFloatVal("1.0"))); else if (IS_FIXED16X16(rvtype)) - ic = newiCode('+', rv, operandFromValue(constFixed16x16Val("1.0"))); + ic = newiCode('+', srcOp, operandFromValue(constFixed16x16Val("1.0"))); else if (IS_BOOL(rvtype)) ic = newiCode('=', NULL, operandFromLit(1)); else - ic = newiCode('+', rv, operandFromLit(size)); + ic = newiCode('+', srcOp, operandFromLit(size)); IC_RESULT(ic) = result = newiTempOperand(rvtype, 0); ADDTOCHAIN(ic); @@ -2363,7 +2527,8 @@ operand *geniCodePreInc(operand *op, bool lvalue) { ADDTOCHAIN(ic); (void)geniCodeAssign(op, result, 0, 0); - if (lvalue || IS_TRUE_SYMOP(op) || IS_BITVAR(optype)) + if (lvalue || (IS_TRUE_SYMOP(op) && !isOperandVolatile(op, FALSE)) || + IS_BITVAR(optype)) return op; else return result; @@ -2384,6 +2549,7 @@ operand *geniCodePostDec(operand *op) { : op); sym_link *rvtype = operandType(rv); int size = 0; + operand *srcOp = rv; /* if this is not an address we have trouble */ if (!op->isaddr) { @@ -2399,17 +2565,23 @@ operand *geniCodePostDec(operand *op) { geniCodeAssign(rOp, rv, 0, 0); + /* If rv is volatile, we can only read it once, and we've just */ + /* done that, so use the copy in rOp instead to avoid reading */ + /* it again. */ + if (isOperandVolatile(rv, FALSE)) + srcOp = rOp; + size = (IS_PTR(rvtype) ? getSize(rvtype->next) : 1); if (size == 0) werror(W_SIZEOF_VOID); if (IS_FLOAT(rvtype)) - ic = newiCode('-', rv, operandFromValue(constFloatVal("1.0"))); + ic = newiCode('-', srcOp, operandFromValue(constFloatVal("1.0"))); else if (IS_FIXED16X16(rvtype)) - ic = newiCode('-', rv, operandFromValue(constFixed16x16Val("1.0"))); + ic = newiCode('-', srcOp, operandFromValue(constFixed16x16Val("1.0"))); else if (IS_BOOL(rvtype)) - ic = newiCode('!', rv, 0); + ic = newiCode('!', srcOp, 0); else - ic = newiCode('-', rv, operandFromLit(size)); + ic = newiCode('-', srcOp, operandFromLit(size)); IC_RESULT(ic) = result = newiTempOperand(rvtype, 0); ADDTOCHAIN(ic); @@ -2454,7 +2626,8 @@ operand *geniCodePreDec(operand *op, bool lvalue) { ADDTOCHAIN(ic); (void)geniCodeAssign(op, result, 0, 0); - if (lvalue || IS_TRUE_SYMOP(op) || IS_BITVAR(optype)) + if (lvalue || (IS_TRUE_SYMOP(op) && !isOperandVolatile(op, FALSE)) || + IS_BITVAR(optype)) return op; else return result; @@ -2467,8 +2640,12 @@ operand *geniCodeBitwise(operand *left, operand *right, int oper, sym_link *resType) { iCode *ic; - left = geniCodeCast(resType, left, TRUE); - right = geniCodeCast(resType, right, TRUE); + /* Signedness doesn't matter for bit ops, so omit */ + /* possible cast if that is the only difference */ + if (compareType(resType, operandType(left)) != -2) + left = geniCodeCast(resType, left, TRUE); + if (compareType(resType, operandType(right)) != -2) + right = geniCodeCast(resType, right, TRUE); ic = newiCode(oper, left, right); IC_RESULT(ic) = newiTempOperand(resType, 0); @@ -2578,6 +2755,20 @@ operand *geniCodeDerefPtr(operand *op, int lvl) { if (IS_TRUE_SYMOP(op)) { op->isaddr = 1; op = geniCodeRValue(op, TRUE); + } else if (IS_OP_LITERAL(op)) { + /* To avoid problems converting a dereferenced literal pointer */ + /* back and forth between lvalue and rvalue formats, replace */ + /* the literal pointer with an iTemp and assign the literal */ + /* value to the iTemp. */ + iCode *ic; + operand *iop = newiTempOperand(optype, 0); + SPEC_SCLS(OP_SYM_ETYPE(iop)) = S_AUTO; /* remove S_LITERAL */ + iop->isaddr = 0; /* assign to the iTemp itself */ + ic = newiCode('=', NULL, op); + IC_RESULT(ic) = iop; + ADDTOCHAIN(ic); + op = operandFromOperand(iop); /* now use the iTemp as operand */ + optype = operandType(op); } /* now get rid of the pointer part */ @@ -2634,9 +2825,8 @@ operand *geniCodeLeftShift(operand *left, operand *right, iCode *ic; sym_link *resType; - ic = newiCode(LEFT_OP, left, right); - resType = usualBinaryConversions(&left, &right, resultType, LEFT_OP); + ic = newiCode(LEFT_OP, left, right); IC_RESULT(ic) = newiTempOperand(resType, 0); ADDTOCHAIN(ic); return IC_RESULT(ic); @@ -2678,6 +2868,17 @@ static operand *geniCodeLogic(operand *left, operand *right, int op, } } + /* Avoid expensive comparisons when the type of the constant is bigger than + * the type of the non-const operand */ + if (IS_INTEGRAL(ltype) && IS_LITERAL(rtype) && + getSize(ltype) < getSize(rtype)) + right->svt.valOperand = valCastLiteral(ltype, operandLitValue(right), + operandLitValueUll(right)); + if (IS_INTEGRAL(rtype) && IS_LITERAL(ltype) && + getSize(rtype) < getSize(ltype)) + left->svt.valOperand = + valCastLiteral(rtype, operandLitValue(left), operandLitValueUll(left)); + /* if one operand is a pointer and the other is a literal generic void pointer, change the type of the literal generic void pointer to match the other pointer */ @@ -2694,8 +2895,9 @@ static operand *geniCodeLogic(operand *left, operand *right, int op, } /* if casting literal to generic pointer, then cast to rtype instead */ if (ic && (ic->op == CAST) && isOperandLiteral(IC_RIGHT(ic))) { - left = operandFromValue( - valCastLiteral(rtype, operandLitValue(IC_RIGHT(ic)))); + left = + operandFromValue(valCastLiteral(rtype, operandLitValue(IC_RIGHT(ic)), + operandLitValueUll(IC_RIGHT(ic)))); ltype = operandType(left); } } @@ -2712,8 +2914,9 @@ static operand *geniCodeLogic(operand *left, operand *right, int op, } /* if casting literal to generic pointer, then cast to rtype instead */ if (ic && (ic->op == CAST) && isOperandLiteral(IC_RIGHT(ic))) { - right = operandFromValue( - valCastLiteral(ltype, operandLitValue(IC_RIGHT(ic)))); + right = + operandFromValue(valCastLiteral(ltype, operandLitValue(IC_RIGHT(ic)), + operandLitValueUll(IC_RIGHT(ic)))); rtype = operandType(right); } } @@ -2896,7 +3099,7 @@ static operand *checkTypes(operand *left, operand *right) { if (always_cast || compareType(ltype, rtype) == -1) right = geniCodeCast(ltype, right, TRUE); - checkPtrQualifiers(ltype, rtype); + checkPtrQualifiers(ltype, rtype, !right->isConstElimnated); return right; } @@ -3063,7 +3266,12 @@ value *geniCodeParms(ast *parms, value *argVals, int *iArg, int *stack, ic = newiCode(IPUSH, pval, NULL); ic->parmPush = 1; /* update the stack adjustment */ - *stack += getSize(IS_AGGREGATE(p) ? aggrToPtr(p, FALSE) : p); + *stack += getSize(IS_ARRAY(p) ? aggrToPtr(p, FALSE) : p); + if ((IFFUNC_ISSMALLC(ftype)) && !IS_AGGREGATE(p) && + getSize(p) == + 1) /* SmallC calling convention passes 8-bit parameters as 16-bit + values. So does pdk due to stack alignment requirements */ + (*stack)++; ADDTOCHAIN(ic); } } @@ -3087,12 +3295,28 @@ operand *geniCodeCall(operand *left, ast *parms, int lvl) { int stack = 0; int iArg = 0; + if (IS_ARRAY(operandType(left))) { + iCode *tic; + sym_link *ttype; + + tic = newiCode(GET_VALUE_AT_ADDRESS, left, operandFromLit(0)); + ttype = copyLinkChain(operandType(left)->next); + IC_RESULT(tic) = newiTempOperand(ttype, 1); + IC_RESULT(tic)->isaddr = IS_FUNCPTR(ttype) ? 1 : 0; + ADDTOCHAIN(tic); + left = IC_RESULT(tic); + } + ftype = operandType(left); if (!IS_FUNC(ftype) && !IS_FUNCPTR(ftype)) { werror(E_FUNCTION_EXPECTED); return operandFromValue(valueFromLit(0)); } + // not allow call a critical function + if (inCriticalPair && FUNC_ISCRITICAL(ftype)) + werror(E_INVALID_CRITICAL); + /* take care of parameters with side-effecting function calls in them, this is required to take care of overlaying function parameters */ @@ -3169,7 +3393,8 @@ static void geniCodeReceive(value *args, operand *func) { and before liveRange calculation */ if (!sym->addrtaken && !IS_VOLATILE(sym->etype)) { - if ((IN_FARSPACE(SPEC_OCLS(sym->etype))) && options.stackAuto == 0) { + if ((IN_FARSPACE(SPEC_OCLS(sym->etype))) && options.stackAuto == 0 && + (!(options.model == MODEL_FLAT24))) { } else { opl = newiTempOperand(args->type, 0); sym->reqv = opl; @@ -3213,6 +3438,7 @@ void geniCodeFunctionBody(ast *tree, int lvl) { operand *func; char *savefilename; int savelineno; + short functionBlock; /* reset the auto generation */ /* numbers */ @@ -3232,6 +3458,7 @@ void geniCodeFunctionBody(ast *tree, int lvl) { lineno = savelineno; /* create a proc icode */ + functionBlock = block; ic = newiCode(FUNCTION, func, NULL); filename = ic->filename = OP_SYMBOL(func)->fileDef; lineno = ic->lineno = OP_SYMBOL(func)->lineDef; @@ -3247,10 +3474,13 @@ void geniCodeFunctionBody(ast *tree, int lvl) { ast2iCode(tree->right, lvl + 1); /* create a label for return */ + block = functionBlock; geniCodeLabel(returnLabel); /* now generate the end proc */ ic = newiCode(ENDFUNCTION, func, NULL); + ic->filename = OP_SYMBOL(func)->fileDef; + ic->lineno = OP_SYMBOL(func)->lastLine; ic->tree = tree; ADDTOCHAIN(ic); return; @@ -3266,6 +3496,11 @@ void geniCodeReturn(operand *op) { if (currFunc && IFFUNC_ISNORETURN(currFunc->type)) werror(W_NORETURNRETURN); + /* check if a cast is needed */ + if (op && currFunc && currFunc->type && currFunc->type->next) + checkPtrQualifiers(currFunc->type->next, operandType(op), + !op->isConstElimnated); + /* if the operand is present force an rvalue */ if (op) op = geniCodeRValue(op, FALSE); @@ -3331,8 +3566,6 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { iCode *ic; symbol *falseLabel; set *labels = NULL; - int needRangeCheck = - !optimize.noJTabBoundary || tree->values.switchVals.swDefault; sym_link *cetype = getSpec(operandType(cond)); int sizeofMinCost, sizeofZeroMinCost, sizeofMaxCost; int sizeofMatchJump, sizeofJumpTable; @@ -3357,6 +3590,8 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { max = (int)ulFromVal(vch); maxVal = vch; + if (max - min < 0) + return 0; /* Exit if the range is too large to handle with a jump table. */ if (1 + max - min > port->jumptableCost.maxCount) return 0; @@ -3379,13 +3614,13 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { sizeofMinCost = 0; sizeofZeroMinCost = 0; sizeofMaxCost = 0; - if (needRangeCheck) { - if (!(min == 0 && IS_UNSIGNED(cetype))) - sizeofMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; - if (!IS_UNSIGNED(cetype)) - sizeofZeroMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; - sizeofMaxCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; - } + + if (!(min == 0 && IS_UNSIGNED(cetype))) + sizeofMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + if (!IS_UNSIGNED(cetype)) + sizeofZeroMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + sizeofMaxCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + if (min) sizeofMinCost += port->jumptableCost.sizeofSubtract; @@ -3413,8 +3648,7 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { sizeofMatchJump = cnt * port->jumptableCost.sizeofMatchJump[sizeIndex]; /* If the size cost of the jump table is uneconomical then exit */ - if (sizeofMatchJump < sizeofJumpTable || - 1 /* SirCmpwn note: this is because relocating a jump table is hard */) + if (sizeofMatchJump < sizeofJumpTable) return 0; /* The jump table is preferable. */ @@ -3458,8 +3692,7 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { } /* first we rule out the boundary conditions */ - /* if only optimization says so */ - if (needRangeCheck) { + { operand *lit; operand *boundary; sym_link *cetype = getSpec(operandType(cond)); @@ -3469,7 +3702,7 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { if ((checkConstantRange(cetype, caseVals->etype, '<', FALSE) != CCR_ALWAYS_FALSE) && (!(min == 0 && IS_UNSIGNED(cetype)))) { - lit = operandFromValue(valCastLiteral(cetype, min)); + lit = operandFromValue(valCastLiteral(cetype, min, min)); boundary = geniCodeLogic(cond, lit, '<', NULL); ic = newiCodeCondition(boundary, falseLabel, NULL); ADDTOCHAIN(ic); @@ -3478,7 +3711,7 @@ int geniCodeJumpTable(operand *cond, value *caseVals, ast *tree) { /* now for upper bounds */ if (checkConstantRange(cetype, maxVal->etype, '>', FALSE) != CCR_ALWAYS_FALSE) { - lit = operandFromValue(valCastLiteral(cetype, max)); + lit = operandFromValue(valCastLiteral(cetype, max, max)); boundary = geniCodeLogic(cond, lit, '>', NULL); ic = newiCodeCondition(boundary, falseLabel, NULL); ADDTOCHAIN(ic); @@ -3552,9 +3785,8 @@ void geniCodeSwitch(ast *tree, int lvl) { } /* if we can make this a jump table */ - if (geniCodeJumpTable(cond, caseVals, tree)) { + if (geniCodeJumpTable(cond, caseVals, tree)) goto jumpTable; /* no need for the comparison */ - } /* for the cases defined do */ while (caseVals) { @@ -3650,6 +3882,7 @@ static void geniCodeCritical(ast *tree, int lvl) { /* the stack. Otherwise, it will be saved in op. */ /* Generate a save of the current interrupt state & disable */ + inCriticalPair = 1; ic = newiCode(CRITICAL, NULL, NULL); IC_RESULT(ic) = op; ADDTOCHAIN(ic); @@ -3663,6 +3896,7 @@ static void geniCodeCritical(ast *tree, int lvl) { /* Generate a restore of the original interrupt state */ ic = newiCode(ENDCRITICAL, NULL, op); ADDTOCHAIN(ic); + inCriticalPair = 0; } /*-----------------------------------------------------------------*/ @@ -3778,7 +4012,9 @@ operand *ast2iCode(ast *tree, int lvl) { IS_ADDRESS_OF_OP(tree)) { addLvaluereq(lvl); if ((!IS_ADDRESS_OF_OP(tree) && IS_ARRAY_OP(tree->left) && - IS_ARRAY_OP(tree->left->left)) || + IS_ARRAY_OP(tree->left->left) && tree->left->left->ftype && + IS_ARRAY(tree->left->left->ftype) && tree->left->left->ftype->next && + IS_ARRAY(tree->left->left->ftype->next)) || (IS_DEREF_OP(tree) && IS_ARRAY_OP(tree->left))) clearLvaluereq(); @@ -3859,7 +4095,7 @@ operand *ast2iCode(ast *tree, int lvl) { case '/': return geniCodeDivision(geniCodeRValue(left, FALSE), geniCodeRValue(right, FALSE), - getResultTypeFromType(tree->ftype)); + getResultTypeFromType(tree->ftype), false); case '%': return geniCodeModulus(geniCodeRValue(left, FALSE), @@ -3988,7 +4224,6 @@ operand *ast2iCode(ast *tree, int lvl) { right = geniCodeRValue(right, TRUE); else right = geniCodeRValue(right, FALSE); - return geniCodeAssign(left, right, 0, 1); } case MUL_ASSIGN: @@ -4004,7 +4239,7 @@ operand *ast2iCode(ast *tree, int lvl) { left, geniCodeDivision(geniCodeRValue(operandFromOperand(left), FALSE), geniCodeRValue(right, FALSE), - getResultTypeFromType(tree->ftype)), + getResultTypeFromType(tree->ftype), false), 0, 1); case MOD_ASSIGN: return geniCodeAssign( @@ -4184,7 +4419,7 @@ const operand *validateOpTypeConst(const operand *op, const char *macro, return op; } fprintf(stderr, - "Internal error: validateOpType failed in %s(%s) @ %s:%u:" + "Internal error: validateOpTypeConst failed in %s(%s) @ %s:%u:" " expected %s, got %s\n", macro, args, file, line, opTypeToStr(type), op ? opTypeToStr(op->type) : "null op"); diff --git a/src/SDCCicode.h b/src/SDCCicode.h index efa707ddb..872a0be5c 100644 --- a/src/SDCCicode.h +++ b/src/SDCCicode.h @@ -66,12 +66,13 @@ typedef struct operand { unsigned int aggr2ptr : 2; /* 1: must change aggregate to pointer to aggregate */ /* 2: aggregate has been changed to pointer to aggregate */ - unsigned int isvolatile : 1; /* is a volatile operand */ - unsigned int isGlobal : 1; /* is a global operand */ - unsigned int isPtr : 1; /* is assigned a pointer */ - unsigned int isGptr : 1; /* is a generic pointer */ - unsigned int isParm : 1; /* is a parameter */ - unsigned int isLiteral : 1; /* operand is literal */ + unsigned int isvolatile : 1; /* is a volatile operand */ + unsigned int isGlobal : 1; /* is a global operand */ + unsigned int isPtr : 1; /* is assigned a pointer */ + unsigned int isGptr : 1; /* is a generic pointer */ + unsigned int isParm : 1; /* is a parameter */ + unsigned int isLiteral : 1; /* operand is literal */ + unsigned int isConstElimnated : 1; /* if original const casted to non-const */ int key; union { @@ -99,6 +100,9 @@ extern const operand *validateOpTypeConst(const operand *op, const char *macro, ->svt.symOperand #define OP_VALUE(op) \ validateOpType(op, "OP_VALUE", #op, VALUE, __FILE__, __LINE__)->svt.valOperand +#define OP_VALUE_CONST(op) \ + validateOpTypeConst(op, "OP_VALUE", #op, VALUE, __FILE__, __LINE__) \ + ->svt.valOperand #define OP_SYM_TYPE(op) \ validateOpType(op, "OP_SYM_TYPE", #op, SYMBOL, __FILE__, __LINE__) \ ->svt.symOperand->type @@ -137,20 +141,24 @@ extern const operand *validateOpTypeConst(const operand *op, const char *macro, #define IC_ARRAYILIST(x) (x)->arrayInitList typedef struct iCode { - unsigned int op; /* operation defined */ - int key; /* running key for this iCode */ - int seq; /* sequence number within routine */ - int seqPoint; /* sequence point */ - short depth; /* loop depth of this iCode */ - short level; /* scope level */ - short block; /* sequential block number */ - unsigned nosupdate : 1; /* don't update spillocation with this */ - unsigned generated : 1; /* code generated for this one */ - unsigned parmPush : 1; /* parameter push Vs spill push */ - unsigned supportRtn : 1; /* will cause a call to a support routine */ - unsigned regsSaved : 1; /* registers have been saved */ - unsigned bankSaved : 1; /* register bank has been saved */ - unsigned builtinSEND : 1; /* SEND for parameter of builtin function */ + unsigned int op; /* operation defined */ + int key; /* running key for this iCode */ + int seq; /* sequence number within routine */ + int seqPoint; /* sequence point */ + short depth; /* loop depth of this iCode */ + long level; /* scope level */ + short block; /* sequential block number */ + unsigned nosupdate : 1; /* don't update spillocation with this */ + unsigned generated : 1; /* code generated for this one */ + unsigned parmPush : 1; /* parameter push Vs spill push */ + unsigned supportRtn : 1; /* will cause a call to a support routine */ + unsigned regsSaved : 1; /* registers have been saved */ + unsigned bankSaved : 1; /* register bank has been saved */ + unsigned builtinSEND : 1; /* SEND for parameter of builtin function */ + bool localEscapeAlive : 1; /* At this iCode, a local variable, a pointer to + which has escaped (e.g. by having been stored in + a global variable, cast to integer, passed to + function) might be alive. */ struct iCode *next; /* next in chain */ struct iCode *prev; /* previous in chain */ @@ -191,11 +199,14 @@ typedef struct iCode { int lineno; /* file & lineno for debug information */ char *filename; - int parmBytes; /* if call/pcall, count of parameter bytes - on stack */ - int argreg; /* argument regno for SEND/RECEIVE */ - int eBBlockNum; /* belongs to which eBBlock */ - char riu; /* after ralloc, the registers in use */ + int parmBytes; /* if call/pcall, count of parameter bytes + on stack */ + int argreg; /* argument regno for SEND/RECEIVE */ + int eBBlockNum; /* belongs to which eBBlock */ + char riu; /* after ralloc, the registers in use */ + float count; /* An execution count or probability */ + float pcount; /* For propagation of count */ + struct ast *tree; /* ast node for this iCode (if not NULL) */ } iCode; @@ -306,7 +317,8 @@ int isOperandEqual(const operand *, const operand *); iCodeTable *getTableEntry(int); int isOperandLiteral(const operand *const); operand *operandOperation(operand *, operand *, int, sym_link *); -double operandLitValue(operand *); +double operandLitValue(const operand *); +unsigned long long operandLitValueUll(const operand *); operand *operandFromLit(double); operand *operandFromOperand(operand *); int isParameterToCall(value *, operand *); @@ -339,6 +351,9 @@ operand *newiTempOperand(sym_link *, char); operand *newiTempFromOp(operand *); iCode *getBuiltinParms(iCode *, int *, operand **); int isiCodeInFunctionCall(iCode *); +operand *detachiCodeOperand(operand **, iCode *); +void attachiCodeOperand(operand *, operand **, iCode *); + /*-----------------------------------------------------------------*/ /* declaration of exported variables */ /*-----------------------------------------------------------------*/ diff --git a/src/SDCClabel.c b/src/SDCClabel.c index b19155f87..4c1a798b6 100644 --- a/src/SDCClabel.c +++ b/src/SDCClabel.c @@ -277,6 +277,28 @@ int labelIfx(iCode *ic) { return change; } +/*-----------------------------------------------------------------*/ +/* replaceGotoGoto - find new target for jump */ +/* if we have a target statement then check if the next */ +/* one is a goto: this means target of goto is a goto */ +/*-----------------------------------------------------------------*/ +static symbol *replaceGotoGoto(const iCode *ic, const symbol *sLabel, + const iCode *target) { + if (!target || !target->next) + return 0; + + if (target->next->op != GOTO && target->next->op != LABEL || + target->next == ic) + return 0; + + symbol *repLabel = target->next->label; + + if (repLabel == sLabel) + return 0; + + return repLabel; +} + /*-----------------------------------------------------------------*/ /* labelGotoGoto - target of a goto is a goto */ /*-----------------------------------------------------------------*/ @@ -287,56 +309,51 @@ int labelGotoGoto(iCode *ic) { for (loop = ic; loop; loop = loop->next) { iCode *stat; symbol *sLabel = NULL; + symbol *repLabel; stat = NULL; switch (loop->op) { case GOTO: /* for a goto statement */ + stat = hTabItemWithKey(labelDef, (sLabel = IC_LABEL(loop))->key); + + if (repLabel = replaceGotoGoto(loop, sLabel, stat)) { + hTabDeleteItem(&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + loop->label = repLabel; + hTabAddItem(&labelRef, repLabel->key, loop); + change++; + } break; + case IFX: /* for a conditional jump */ + if (IC_TRUE(loop)) stat = hTabItemWithKey(labelDef, (sLabel = IC_TRUE(loop))->key); else stat = hTabItemWithKey(labelDef, (sLabel = IC_FALSE(loop))->key); - } - /* if we have a target statement then check if the next */ - /* one is a goto: this means target of goto is a goto */ - if (stat && stat->next && - (stat->next->op == GOTO || stat->next->op == LABEL) && - stat->next != loop) { - - symbol *repLabel = stat->next->label; /* replace with label */ - - /* if they are the same then continue */ - if (repLabel->key == sLabel->key) - continue; - - /* replacement depends on the statement type */ - switch (loop->op) { - - case GOTO: /* for a goto statement */ - - hTabDeleteItem(&labelRef, (IC_LABEL(loop))->key, loop, DELETE_ITEM, - NULL); - loop->label = repLabel; - hTabAddItem(&labelRef, repLabel->key, loop); - break; - - case IFX: /* for a conditional jump */ + if (repLabel = replaceGotoGoto(loop, sLabel, stat)) { if (IC_TRUE(loop)) { - - hTabDeleteItem(&labelRef, (IC_TRUE(loop))->key, loop, DELETE_ITEM, - NULL); + hTabDeleteItem(&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); IC_TRUE(loop) = repLabel; } else { - - hTabDeleteItem(&labelRef, (IC_FALSE(loop))->key, loop, DELETE_ITEM, - NULL); + hTabDeleteItem(&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); IC_FALSE(loop) = repLabel; } hTabAddItem(&labelRef, repLabel->key, loop); + change++; } - change++; + break; + case JUMPTABLE: + + for (sLabel = setFirstItem(IC_JTLABELS(loop)); sLabel; + sLabel = setNextItem(IC_JTLABELS(loop))) + if (repLabel = replaceGotoGoto( + loop, sLabel, hTabItemWithKey(labelDef, sLabel->key))) { + hTabDeleteItem(&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + replaceSetItem(IC_JTLABELS(loop), sLabel, repLabel); + hTabAddItem(&labelRef, repLabel->key, loop); + change++; + } } } diff --git a/src/SDCCloop.c b/src/SDCCloop.c index 651fb6b8a..11b180c76 100644 --- a/src/SDCCloop.c +++ b/src/SDCCloop.c @@ -382,7 +382,6 @@ static DEFSETFUNC(hasNonPtrUse) { static int loopInvariants(region *theLoop, ebbIndex *ebbi) { eBBlock **ebbs = ebbi->dfOrder; int count = ebbi->count; - eBBlock *lBlock; set *lInvars = NULL; int change = 0; @@ -395,11 +394,10 @@ static int loopInvariants(region *theLoop, ebbIndex *ebbi) { /* we will do the elimination for those blocks */ /* in the loop that dominate all exits from the loop */ - for (lBlock = setFirstItem(theLoop->regBlocks); lBlock; + for (eBBlock *lBlock = setFirstItem(theLoop->regBlocks); lBlock; lBlock = setNextItem(theLoop->regBlocks)) { iCode *ic; int domsAllExits; - int i; /* mark the dominates all exits flag */ domsAllExits = (applyToSet(theLoop->exits, dominatedBy, lBlock) == @@ -460,6 +458,9 @@ static int loopInvariants(region *theLoop, ebbIndex *ebbi) { (IC_RIGHT(ic) && isOperandVolatile(IC_RIGHT(ic), TRUE))) continue; + if (POINTER_GET(ic) && IS_VOLATILE(operandType(IC_LEFT(ic))->next)) + continue; + lin = rin = 0; /* special case */ @@ -508,7 +509,7 @@ static int loopInvariants(region *theLoop, ebbIndex *ebbi) { /* for successors for all exits */ for (sBlock = setFirstItem(theLoop->exits); sBlock; sBlock = setNextItem(theLoop->exits)) { - for (i = 0; i < count; ebbs[i++]->visited = 0) + for (int i = 0; i < count; ebbs[i++]->visited = 0) ; lBlock->visited = 1; if (applyToSet(sBlock->succList, isDefAlive, ic)) @@ -601,10 +602,9 @@ static int loopInvariants(region *theLoop, ebbIndex *ebbi) { if (lInvars) { eBBlock *preHdr = theLoop->entry->preHeader; iCode *icFirst = NULL, *icLast = NULL; - cseDef *cdp; /* create an iCode chain from it */ - for (cdp = setFirstItem(lInvars); cdp; cdp = setNextItem(lInvars)) { + for (cseDef *cdp = setFirstItem(lInvars); cdp; cdp = setNextItem(lInvars)) { /* maintain data flow .. add it to the */ /* ldefs defSet & outExprs of the preheader */ preHdr->defSet = bitVectSetBit(preHdr->defSet, cdp->diCode->key); @@ -830,6 +830,11 @@ static void addPostLoopBlock(region *loopReg, ebbIndex *ebbi, iCode *ic) { /* insert goto to old predecessor of eblock */ newic = newiCodeLabelGoto(GOTO, eblock->entryLabel); addiCodeToeBBlock(ebpi, newic, NULL); + /* Make sure the GOTO has a target */ + if (eblock->sch->op != LABEL) { + newic = newiCodeLabelGoto(LABEL, eblock->entryLabel); + addiCodeToeBBlock(eblock, newic, eblock->sch); + } break; /* got it, only one is possible */ } } @@ -929,7 +934,7 @@ static set *basicInduction(region *loopReg, ebbIndex *ebbi) { /* Only consider variables with integral type. */ /* (2004/12/06 - EEP - ds390 fails regression tests unless */ /* pointers are also considered for induction (due to some */ - /* register alloctaion bugs). Remove !IS_PTR clause when */ + /* register allocation bugs). Remove !IS_PTR clause when */ /* that gets fixed) */ optype = operandType(IC_RIGHT(ic)); if (!IS_INTEGRAL(optype) && !IS_PTR(optype)) @@ -1016,6 +1021,7 @@ static set *basicInduction(region *loopReg, ebbIndex *ebbi) { but it's a nice to see a clean dumploop now. */ remiCodeFromeBBlock(lBlock, ic); /* clear the definition */ + bitVectUnSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); bitVectUnSetBit(lBlock->defSet, ic->key); ic = saveic; } else @@ -1098,10 +1104,8 @@ static int loopInduction(region *loopReg, ebbIndex *ebbi) { /* ask port for size not worth if native instruction exist for multiply & divide */ - if (getSize(operandType(IC_LEFT(ic))) <= - (unsigned long)port->support.muldiv || - getSize(operandType(IC_RIGHT(ic))) <= - (unsigned long)port->support.muldiv) + if (port->hasNativeMulFor(ic, operandType(IC_LEFT(ic)), + operandType(IC_RIGHT(ic)))) continue; /* if this is a division then the remainder should be zero @@ -1132,6 +1136,7 @@ static int loopInduction(region *loopReg, ebbIndex *ebbi) { ic->op = '='; IC_LEFT(ic) = NULL; IC_RIGHT(ic) = IC_RESULT(ic); + bitVectUnSetBit(OP_USES(aSym), ic->key); /* Insert an update of the induction variable just before */ /* the update of the basic induction variable. */ diff --git a/src/SDCClospre.cc b/src/SDCClospre.cc index ed8ece9e7..d3037245b 100644 --- a/src/SDCClospre.cc +++ b/src/SDCClospre.cc @@ -20,6 +20,8 @@ // Lifetime-optimal speculative partial redundancy elimination. // #define DEBUG_LOSPRE // Uncomment to get debug messages while doing lospre. +// #define DEBUG_LOSPRE_ASS // Uncomment to get debug messages on considered +// assignmentd while doing lospre. #include "SDCClospre.hpp" @@ -81,17 +83,36 @@ void create_cfg_lospre(cfg_lospre_t &cfg, iCode *start_ic, ebbIndex *ebbi) { static bool candidate_expression(const iCode *const ic, int lkey) { wassert(ic); - if (ic->op != '!' && ic->op != '~' && ic->op != UNARYMINUS && ic->op != '+' && - ic->op != '-' && ic->op != '*' && ic->op != '/' && ic->op != '%' && - ic->op != '>' && ic->op != '<' && ic->op != LE_OP && ic->op != GE_OP && - ic->op != NE_OP && ic->op != EQ_OP && ic->op != AND_OP && - ic->op != OR_OP && ic->op != '^' && ic->op != '|' && - ic->op != BITWISEAND && ic->op != RRC && ic->op != RLC && - ic->op != GETABIT && ic->op != GETHBIT && ic->op != LEFT_OP && - ic->op != RIGHT_OP && - !(ic->op == '=' && !POINTER_SET(ic) && - !(IS_ITEMP(IC_RIGHT(ic)) /*&& IC_RIGHT(ic)->key > lkey*/)) && - ic->op != GET_VALUE_AT_ADDRESS && ic->op != CAST) + if ( + ic->op != '!' && + ic->op != '~' && + ic->op != UNARYMINUS && + ic->op != '+' && + ic->op != '-' && + ic->op != '*' && + ic->op != '/' && + ic->op != '%' && + ic->op != '>' && + ic->op != '<' && + ic->op != LE_OP && + ic->op != GE_OP && + ic->op != NE_OP && + ic->op != EQ_OP && + ic->op != AND_OP && + ic->op != OR_OP && + ic->op != '^' && + ic->op != '|' && + ic->op != BITWISEAND && + ic->op != RRC && + ic->op != RLC && + ic->op != GETABIT && + ic->op != GETHBIT && + ic->op != LEFT_OP && + ic->op != RIGHT_OP && + !(ic->op == '=' && !POINTER_SET(ic) && !(IS_ITEMP(IC_RIGHT(ic)) /*&& IC_RIGHT(ic)->key > lkey*/)) && + ic->op != GET_VALUE_AT_ADDRESS && + ic->op != CAST /*&& + ic->op != ADDRESS_OF Apparently typically not worth the cost in code size*/) return (false); const operand *const left = IC_LEFT(ic); @@ -102,12 +123,6 @@ static bool candidate_expression(const iCode *const ic, int lkey) { if (ic->op == '=' && IS_OP_LITERAL(right)) return (false); - if (IS_OP_VOLATILE(left) || IS_OP_VOLATILE(right)) - return (false); - - if (POINTER_GET(ic) && IS_VOLATILE(operandType(IC_LEFT(ic))->next)) - return (false); - // Todo: Allow more operands! if (ic->op != CAST && left && !(IS_SYMOP(left) || IS_OP_LITERAL(left)) || right && !(IS_SYMOP(right) || IS_OP_LITERAL(right)) || @@ -135,7 +150,8 @@ static bool same_expression(const iCode *const lic, const iCode *const ric) { IS_COMMUTATIVE(lic) && isOperandEqual(lleft, rright) && isOperandEqual(lright, rleft)) && (lresult && rresult && - compareTypeInexact(operandType(lresult), operandType(rresult)) > 0)) + compareTypeInexact(operandType(lresult), operandType(rresult)) > 0) && + IS_FLOAT(operandType(lresult)) == IS_FLOAT(operandType(rresult))) return (true); return (false); @@ -158,9 +174,8 @@ static void get_candidate_set(std::set *c, const iCode *const sic, } } -static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, - const iCode *const eic) { - typedef boost::graph_traits::vertex_descriptor vertex_t; +static bool invalidates_expression(const iCode *const eic, + const iCode *const iic) { const operand *const eleft = IC_LEFT(eic); const operand *const eright = IC_RIGHT(eic); const bool uses_global = @@ -168,6 +183,41 @@ static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, isOperandGlobal(eright) || IS_SYMOP(eleft) && OP_SYMBOL_CONST(eleft)->addrtaken || IS_SYMOP(eright) && OP_SYMBOL_CONST(eright)->addrtaken); + const bool uses_volatile = + POINTER_GET(eic) && IS_VOLATILE(operandType(eleft)->next) || + IS_OP_VOLATILE(eleft) || IS_OP_VOLATILE(eright); + + const operand *const left = IC_LEFT(iic); + const operand *const right = IC_RIGHT(iic); + const operand *const result = IC_RESULT(iic); + + if (iic->op == FUNCTION || iic->op == ENDFUNCTION || iic->op == RECEIVE) + return (true); + if (eic->op == ADDRESS_OF) // ADDRESS_OF does not really read its operand. + return (false); + if (eic->op == GET_VALUE_AT_ADDRESS && + (isOperandGlobal(IC_RESULT(iic)) || + IS_SYMOP(IC_RESULT(iic)) && OP_SYMBOL_CONST(IC_RESULT(iic))->addrtaken)) + return (true); + if (IC_RESULT(iic) && !IS_OP_LITERAL(result) && !POINTER_SET(iic) && + (eleft && isOperandEqual(eleft, result) || + eright && isOperandEqual(eright, result))) + return (true); + if ((uses_global || uses_volatile) && (iic->op == CALL || iic->op == PCALL)) + return (true); + if (uses_volatile && + (POINTER_GET(iic) && IS_VOLATILE(operandType(left)->next)) || + IS_OP_VOLATILE(left) || IS_OP_VOLATILE(right)) + return (true); + if (uses_global && POINTER_SET(iic)) // TODO: More accuracy here! + return (true); + + return (false); +} + +static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, + const iCode *const eic) { + typedef boost::graph_traits::vertex_descriptor vertex_t; bool safety_required = false; // In redundancy elimination, safety means not doing a computation on any path @@ -181,7 +231,7 @@ static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, // require safety, since adding two undefined operands gives just another // undefined (the C standard allows trap representations, which, could result // in addition requiring safety though; AFAIK none of the targets currently - // supported by sdcc have trap representations). Philipp, 2012-07-06. + // supported by SDCC have trap representations). Philipp, 2012-07-06. // // For now we just always require safety for "dangerous" operations. // @@ -193,9 +243,14 @@ static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, if (eic->op == CALL || eic->op == PCALL) safety_required = true; + // volatile requires safety. + if (POINTER_GET(eic) && IS_VOLATILE(operandType(IC_LEFT(eic))->next) || + IS_OP_VOLATILE(IC_LEFT(eic)) || IS_OP_VOLATILE(IC_RIGHT(eic))) + safety_required = true; + // Reading from an invalid address might be dangerous, since there could be // memory-mapped I/O. - if (eic->op == GET_VALUE_AT_ADDRESS && !optimize.lospre_unsafe_read) + if (eic->op == GET_VALUE_AT_ADDRESS && !optimize.allow_unsafe_read) safety_required = true; // TODO: Relax this! There are cases where allowing unsafe optimizations will @@ -204,23 +259,25 @@ static bool setup_cfg_for_expression(cfg_lospre_t *const cfg, if (optimize.codeSpeed) safety_required = true; +#ifdef DEBUG_LOSPRE + std::cout << "Invalidation set I: "; +#endif for (vertex_t i = 0; i < boost::num_vertices(*cfg); i++) { const iCode *const ic = (*cfg)[i].ic; + (*cfg)[i].uses = same_expression(eic, ic); - (*cfg)[i].invalidates = false; - if (IC_RESULT(ic) && !IS_OP_LITERAL(IC_RESULT(ic)) && !POINTER_SET(ic) && - (eleft && isOperandEqual(eleft, IC_RESULT(ic)) || - eright && isOperandEqual(eright, IC_RESULT(ic)))) - (*cfg)[i].invalidates = true; - if (ic->op == FUNCTION || ic->op == ENDFUNCTION || ic->op == RECEIVE) - (*cfg)[i].invalidates = true; - if (uses_global && (ic->op == CALL || ic->op == PCALL)) - (*cfg)[i].invalidates = true; - if (uses_global && POINTER_SET(ic)) // TODO: More accuracy here! - (*cfg)[i].invalidates = true; + (*cfg)[i].invalidates = invalidates_expression(eic, ic); (*cfg)[i].forward = std::pair(-1, -1); + +#ifdef DEBUG_LOSPRE + if ((*cfg)[i].invalidates) + std::cout << i << ", "; +#endif } +#ifdef DEBUG_LOSPRE + std::cout << "\n"; +#endif return (safety_required); } @@ -242,38 +299,40 @@ void dump_cfg_lospre(const cfg_lospre_t &cfg) { dbuf_free(iLine); name[i] = os.str(); } - boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name)); + boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name), + boost::default_writer(), + cfg_titlewriter(currFunc->rname, "lospre")); delete[] name; } -#if 0 // Dump tree decomposition. -static void dump_tree_decomposition(const tree_dec_lospre_t &tree_dec) -{ - std::ofstream dump_file((std::string(dstFileName) + ".dumplospredec" + currFunc->rname + ".dot").c_str()); +static void dump_dec_lospre(const tree_dec_t &tree_dec) { + wassert(currFunc); + + std::ofstream dump_file( + (std::string(dstFileName) + ".dumplospredec" + currFunc->rname + ".dot") + .c_str()); unsigned int w = 0; std::string *name = new std::string[num_vertices(tree_dec)]; - for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) - { - if (tree_dec[i].bag.size() > w) - w = tree_dec[i].bag.size(); - std::ostringstream os; - std::set::const_iterator v1; - os << i << " | "; - for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) - os << *v1 << " "; - name[i] = os.str(); - } + for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) { + if (tree_dec[i].bag.size() > w) + w = tree_dec[i].bag.size(); + std::ostringstream os; + typename decltype(tree_dec[0].bag)::const_iterator v1; + os << i << " | "; + for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) + os << *v1 << " "; + name[i] = os.str(); + } boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name)); delete[] name; } -#endif void lospre(iCode *sic, ebbIndex *ebbi) { cfg_lospre_t control_flow_graph; - tree_dec_lospre_t tree_decomposition; + tree_dec_t tree_decomposition; wassert(sic); @@ -287,8 +346,10 @@ void lospre(iCode *sic, ebbIndex *ebbi) { if (options.dump_graphs) dump_cfg_lospre(control_flow_graph); - thorup_tree_decomposition(tree_decomposition, control_flow_graph); - nicify(tree_decomposition); + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + if (options.dump_graphs) + dump_dec_lospre(tree_decomposition); int lkey = operandKey; diff --git a/src/SDCClospre.hpp b/src/SDCClospre.hpp index a145e7a64..aa0140ef1 100644 --- a/src/SDCClospre.hpp +++ b/src/SDCClospre.hpp @@ -19,32 +19,28 @@ // // Lifetime-optimal speculative partial redundancy elimination. +// Workaround for boost bug #11880 +#include +#if BOOST_VERSION == 106000 +#include +#endif + #include #include #include -#include "SDCCtree_dec.hpp" - extern "C" { +#include "SDCCsymt.h" +#include "SDCCicode.h" #include "SDCCBBlock.h" #include "SDCCasm.h" #include "SDCCgen.h" -#include "SDCCicode.h" #include "SDCCopt.h" -#include "SDCCsymt.h" #include "SDCCy.h" #include "port.h" } -#ifdef HAVE_STX_BTREE_SET_H -#include -#endif - -#if 0 // def HAVE_STX_BTREE_SET_H -typedef stx::btree_set lospreset_t; // Faster than std::set -#else -typedef std::set lospreset_t; -#endif +typedef std::set lospreset_t; struct assignment_lospre { boost::tuple @@ -59,6 +55,8 @@ struct assignment_lospre { ai_end = a.local.end(); for (i = local.begin(), ai = a.local.begin();; ++i, ++ai) { + if (i == i_end && ai == ai_end) + return (false); if (i == i_end) return (true); if (ai == ai_end) @@ -102,7 +100,8 @@ struct cfg_lospre_node { typedef std::list assignment_list_lospre_t; struct tree_dec_lospre_node { - std::set bag; + lospreset_t bag; + assignment_list_lospre_t assignments; unsigned weight; // The weight is the number of nodes at which intermediate // results need to be remembered. In general, to minimize @@ -120,9 +119,16 @@ typedef boost::adjacency_list - tree_dec_lospre_t; + tree_dec_t; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif -#if 1 +#include "SDCCtree_dec.hpp" + +#ifdef DEBUG_LOSPRE void print_assignment(const assignment_lospre &a, cfg_lospre_t G) { wassert(a.global.size() == boost::num_vertices(G)); for (unsigned int i = 0; i < boost::num_vertices(G); i++) @@ -161,29 +167,27 @@ int tree_dec_lospre_introduce( assignment_list_lospre_t::iterator ai; boost::tie(c, c_end) = adjacent_vertices(t, T); - assignment_list_lospre_t &alist2 = T[t].assignments; - assignment_list_lospre_t &alist = T[*c].assignments; + assignment_list_lospre_t &alist = T[t].assignments; + std::swap(alist, T[*c].assignments); if (alist.size() > size_t(options.max_allocs_per_node) / 2) { alist.clear(); return (-1); } - std::set new_inst; + lospreset_t new_inst; std::set_difference(T[t].bag.begin(), T[t].bag.end(), T[*c].bag.begin(), T[*c].bag.end(), std::inserter(new_inst, new_inst.end())); - unsigned short int i = *(new_inst.begin()); + unsigned int i = *(new_inst.begin()); for (ai = alist.begin(); ai != alist.end(); ++ai) { ai->local.insert(i); ai->global[i] = false; - alist2.push_back(*ai); + ai = alist.insert(ai, *ai); + ++ai; ai->global[i] = true; - alist2.push_back(*ai); } - alist.clear(); - return (0); } @@ -197,14 +201,19 @@ void tree_dec_lospre_forget( adjacency_iter_t c, c_end; boost::tie(c, c_end) = adjacent_vertices(t, T); +#ifdef DEBUG_LOSPRE_ASS + std::cout << "Forget (" << t << "):\n"; + std::cout.flush(); +#endif + assignment_list_lospre_t &alist = T[t].assignments; std::swap(alist, T[*c].assignments); - std::set old_inst; + lospreset_t old_inst; std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); - unsigned short int i = *(old_inst.begin()); + unsigned int i = *(old_inst.begin()); assignment_list_lospre_t::iterator ai, aif; @@ -243,6 +252,13 @@ void tree_dec_lospre_forget( alist.sort(); +#ifdef DEBUG_LOSPRE_ASS + for (ai = alist.begin(); ai != alist.end(); ++ai) { + print_assignment(*ai, G); + std::cout << "\n"; + } +#endif + // Collapse (locally) identical assignments. for (ai = alist.begin(); ai != alist.end();) { aif = ai; @@ -253,16 +269,25 @@ void tree_dec_lospre_forget( alist.erase(aif); aif = ai; ++ai; - } else { - alist.erase(ai); - ai = aif; - ++ai; - } + } else + ai = alist.erase(ai); } } if (!alist.size()) std::cerr << "No surviving assignments at forget node (lospre).\n"; + +#ifdef DEBUG_LOSPRE + std::cout << "Remaining assignments: " << alist.size() << "\n"; + std::cout.flush(); +#endif + +#ifdef DEBUG_LOSPRE_ASS + for (ai = alist.begin(); ai != alist.end(); ++ai) { + print_assignment(*ai, G); + std::cout << "\n"; + } +#endif } // Handle join nodes in the nice tree decomposition @@ -279,36 +304,33 @@ void tree_dec_lospre_join( ++c; c3 = c; - assignment_list_lospre_t &alist1 = T[t].assignments; + assignment_list_lospre_t &alist = T[t].assignments; assignment_list_lospre_t &alist2 = T[*c2].assignments; - assignment_list_lospre_t &alist3 = T[*c3].assignments; + std::swap(alist, T[*c3].assignments); + alist.sort(); alist2.sort(); - alist3.sort(); - - assignment_list_lospre_t::iterator ai2, ai3; - for (ai2 = alist2.begin(), ai3 = alist3.begin(); - ai2 != alist2.end() && ai3 != alist3.end();) { - if (assignments_lospre_locally_same(*ai2, *ai3)) { - ai2->s.get<0>() += ai3->s.get<0>(); - ai2->s.get<1>() += ai3->s.get<1>(); - for (size_t i = 0; i < ai2->global.size(); i++) - ai2->global[i] = (ai2->global[i] || ai3->global[i]); - alist1.push_back(*ai2); + assignment_list_lospre_t::iterator ai, ai2; + for (ai = alist.begin(), ai2 = alist2.begin(); + ai != alist.end() && ai2 != alist2.end();) { + if (assignments_lospre_locally_same(*ai, *ai2)) { + ai->s.get<0>() += ai2->s.get<0>(); + ai->s.get<1>() += ai2->s.get<1>(); + for (size_t i = 0; i < ai->global.size(); i++) + ai->global[i] = (ai->global[i] || ai2->global[i]); + + ++ai; ++ai2; - ++ai3; - } else if (*ai2 < *ai3) { + } else if (*ai < *ai2) + ai = alist.erase(ai); + else if (*ai2 < *ai) ++ai2; - continue; - } else if (*ai3 < *ai2) { - ++ai3; - continue; - } } + while (ai != alist.end()) + ai = alist.erase(ai); alist2.clear(); - alist3.clear(); } template @@ -368,10 +390,10 @@ void tree_dec_safety_forget( std::swap(alist, T[*c].assignments); - std::set old_inst; + lospreset_t old_inst; std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); - unsigned short int i = *(old_inst.begin()); + unsigned int i = *(old_inst.begin()); assignment_list_lospre_t::iterator ai, aif; @@ -402,7 +424,8 @@ void tree_dec_safety_forget( for (ok = false, boost::tie(n, n_end) = boost::out_edges(i, G); !ok && n != n_end; ++n) if (ai->global[boost::target(*n, G)] || - G[boost::target(*n, G)].invalidates) + G[boost::target(*n, G)].invalidates && + !G[boost::target(*n, G)].uses) ok = true; if (!ok) { @@ -505,9 +528,9 @@ int tree_dec_safety_nodes( } template -static void split_edge(T_t &T, G_t &G, - typename boost::graph_traits::edge_descriptor e, - const iCode *ic, operand *tmpop) { +typename boost::graph_traits::vertex_descriptor +split_edge(T_t &T, G_t &G, typename boost::graph_traits::edge_descriptor e, + const iCode *ic, operand *tmpop) { // Insert new iCode into chain. iCode *newic = newiCode(ic->op, IC_LEFT(ic), IC_RIGHT(ic)); IC_RESULT(newic) = tmpop; @@ -515,6 +538,7 @@ static void split_edge(T_t &T, G_t &G, newic->lineno = ic->lineno; newic->prev = G[boost::source(e, G)].ic; newic->next = G[boost::target(e, G)].ic; + newic->count = G[boost::source(e, G)].ic->count; G[boost::source(e, G)].ic->next = newic; G[boost::target(e, G)].ic->prev = newic; @@ -526,11 +550,12 @@ static void split_edge(T_t &T, G_t &G, // Insert node into cfg. typename boost::graph_traits::vertex_descriptor n = boost::add_vertex(G); - // TODO: Exact cost. + G[n].ic = newic; G[n].uses = false; + G[n].invalidates = false; boost::add_edge(boost::source(e, G), n, G[e], G); - boost::add_edge(n, boost::target(e, G), 3.0, G); + boost::add_edge(n, boost::target(e, G), G[e], G); #ifdef DEBUG_LOSPRE std::cout << "Calculating " << OP_SYMBOL_CONST(tmpop)->name << " at ic " @@ -559,6 +584,8 @@ static void split_edge(T_t &T, G_t &G, // Remove old edge from cfg. boost::remove_edge(e, G); + + return (n); } template @@ -579,8 +606,11 @@ static void forward_lospre_assignment( iCode *nic = G[i].ic; - if (isOperandEqual(IC_RESULT(ic), IC_LEFT(nic)) && nic->op != ADDRESS_OF && - (!POINTER_GET(nic) || !IS_PTR(operandType(IC_RESULT(nic))) || + if (isOperandEqual(IC_RESULT(ic), IC_LEFT(nic)) && + IS_UNSIGNED(operandType(tmpop)) == + IS_UNSIGNED(operandType(IC_LEFT(nic))) && + nic->op != ADDRESS_OF && nic->op != PCALL && + (!POINTER_GET(nic) || !IS_PTR(operandType(IC_LEFT(nic))) || !IS_BITFIELD(operandType(IC_LEFT(nic))->next) || compareType(operandType(IC_LEFT(nic)), operandType(tmpop)) == 1)) { bool isaddr = IC_LEFT(nic)->isaddr; @@ -589,7 +619,7 @@ static void forward_lospre_assignment( << OP_SYMBOL_CONST(IC_LEFT(nic))->name << " at " << nic->key << "\n"; #endif - // bitVectUnSetBit (OP_SYMBOL (IC_LEFT (nic))->uses, nic->key); + bitVectUnSetBit(OP_SYMBOL(IC_LEFT(nic))->uses, nic->key); IC_LEFT(nic) = operandFromOperand(tmpop); // bitVectSetBit (OP_SYMBOL (IC_LEFT (nic))->uses, nic->key); IC_LEFT(nic)->isaddr = isaddr; @@ -600,7 +630,7 @@ static void forward_lospre_assignment( << OP_SYMBOL_CONST(IC_RIGHT(nic))->name << " at " << nic->key << "\n"; #endif - // bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (nic))->uses, nic->key); + bitVectUnSetBit(OP_SYMBOL(IC_RIGHT(nic))->uses, nic->key); IC_RIGHT(nic) = operandFromOperand(tmpop); // bitVectSetBit (OP_SYMBOL (IC_RIGHT (nic))->uses, nic->key); } @@ -613,7 +643,7 @@ static void forward_lospre_assignment( << OP_SYMBOL_CONST(IC_RESULT(nic))->name << " at " << nic->key << "\n"; #endif - // bitVectUnSetBit (OP_SYMBOL (IC_RESULT (nic))->uses, nic->key); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(nic))->uses, nic->key); IC_RESULT(nic) = operandFromOperand(tmpop); IC_RESULT(nic)->isaddr = true; // bitVectSetBit (OP_SYMBOL (IC_RESULT (nic))->uses, nic->key); @@ -644,8 +674,8 @@ static void forward_lospre_assignment( adjacency_iter_t c, c_end; for (boost::tie(c, c_end) = boost::adjacent_vertices(i, G); c != c_end; ++c) { - if (!((a.global[i] & true) && !G[i].invalidates) && - (a.global[*c] & true)) // Calculation edge + if (!(a.global[i] && !G[i].invalidates) && + a.global[*c]) // Calculation edge continue; forward_lospre_assignment(G, *c, ic, a); } @@ -655,8 +685,7 @@ static void forward_lospre_assignment( boost::tie(c, c_end) = adjacent_vertices(i, G); if (c == c_end) break; - if (!((a.global[i] & true) && !G[i].invalidates) && - (a.global[*c] & true)) // Calculation edge + if (!(a.global[i] && !G[i].invalidates) && a.global[*c]) // Calculation edge break; i = *c; } @@ -664,7 +693,7 @@ static void forward_lospre_assignment( template static int implement_lospre_assignment( - const assignment_lospre a, T_t &T, G_t &G, + assignment_lospre a, T_t &T, G_t &G, const iCode *ic) // Assignment has to be passed as a copy (not reference), // since the transformations on the tree-decomposition will // invalidate it otherwise. @@ -703,7 +732,10 @@ static int implement_lospre_assignment( for (typename std::set::iterator i = calculation_edges.begin(); i != calculation_edges.end(); ++i) { - split_edge(T, G, *i, ic, tmpop); + typename boost::graph_traits::vertex_descriptor n = + split_edge(T, G, *i, ic, tmpop); + a.global.resize(boost::num_vertices(G)); + a.global[n] = true; split++; } @@ -722,15 +754,16 @@ static int implement_lospre_assignment( (a.global[boost::source(*e, G)] & true))) continue; #ifdef DEBUG_LOSPRE - std::cout << "Substituting ic " << G[*v].ic->key << "\n"; + std::cerr << "Substituting ic " << G[*v].ic->key << "\n"; #endif substituted++; iCode *ic = G[*v].ic; - // if (IC_LEFT (ic) && IS_ITEMP (IC_LEFT (ic))) - // bitVectUnSetBit (OP_SYMBOL (IC_LEFT (ic))->uses, ic->key); - // if (IC_RIGHT (ic) && IS_ITEMP (IC_RIGHT (ic))) - // bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + + if (IS_SYMOP(IC_LEFT(ic))) + bitVectUnSetBit(OP_SYMBOL(IC_LEFT(ic))->uses, ic->key); + if (IS_SYMOP(IC_RIGHT(ic))) + bitVectUnSetBit(OP_SYMBOL(IC_RIGHT(ic))->uses, ic->key); IC_RIGHT(ic) = tmpop; // bitVectSetBit (OP_SYMBOL (IC_RIGHT(ic))->uses, ic->key); if (!POINTER_SET(ic)) { @@ -770,8 +803,8 @@ static int implement_lospre_assignment( /* Using a template here confuses debugging tools such as valgrind. */ /*template */ -static int tree_dec_lospre(tree_dec_lospre_t /*T_t*/ &T, - cfg_lospre_t /*G_t*/ &G, const iCode *ic) { +static int tree_dec_lospre(tree_dec_t /*T_t*/ &T, cfg_lospre_t /*G_t*/ &G, + const iCode *ic) { if (tree_dec_lospre_nodes(T, find_root(T), G)) return (-1); @@ -779,8 +812,10 @@ static int tree_dec_lospre(tree_dec_lospre_t /*T_t*/ &T, T[find_root(T)].assignments.end()); const assignment_lospre &winner = *(T[find_root(T)].assignments.begin()); - // std::cout << "Winner (lospre): "; - // print_assignment(winner, G); +#ifdef DEBUG_LOSPRE + std::cout << "Winner (lospre): "; + print_assignment(winner, G); +#endif int change; if (change = implement_lospre_assignment(winner, T, G, ic)) @@ -800,8 +835,8 @@ static void implement_safety(const assignment_lospre &a, G_t &G) { /* Using a template here confuses debugging tools such as valgrind. */ /*template */ -static int tree_dec_safety(tree_dec_lospre_t /*T_t*/ &T, - cfg_lospre_t /*G_t*/ &G, const iCode *ic) { +static int tree_dec_safety(tree_dec_t /*T_t*/ &T, cfg_lospre_t /*G_t*/ &G, + const iCode *ic) { if (tree_dec_safety_nodes(T, find_root(T), G)) return (-1); @@ -812,7 +847,7 @@ static int tree_dec_safety(tree_dec_lospre_t /*T_t*/ &T, implement_safety(winner, G); #ifdef DEBUG_LOSPRE - std::cout << "Winner (safety): "; + std::cout << "Winner (safety) (I' \\ I): "; print_assignment(winner, G); #endif diff --git a/src/SDCClrange.c b/src/SDCClrange.c index 212da6a26..ee5db6ddc 100644 --- a/src/SDCClrange.c +++ b/src/SDCClrange.c @@ -385,8 +385,10 @@ static bool findPrevUseSym(eBBlock *ebp, iCode *ic, symbol *sym) { /* - fix the life range, if the symbol is used in */ /* a loop */ /*------------------------------------------------------------------*/ -static void findPrevUse(eBBlock *ebp, iCode *ic, operand *op, eBBlock **ebbs, - int count, bool emitWarnings) { +static int findPrevUse(eBBlock *ebp, iCode *ic, operand *op, eBBlock **ebbs, + int count, bool emitWarnings) { + int change = 0; + unvisitBlocks(ebbs, count); if (op->isaddr) @@ -399,13 +401,80 @@ static void findPrevUse(eBBlock *ebp, iCode *ic, operand *op, eBBlock **ebbs, if (emitWarnings) { if (IS_ITEMP(op)) { if (OP_SYMBOL(op)->prereqv) { + iCode *newic, *ip; + value *val; + bitVect *dom = NULL; + bitVect *used; + int i, blocknum; werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, OP_SYMBOL(op)->prereqv->name); - OP_SYMBOL(op)->prereqv->reqv = NULL; - OP_SYMBOL(op)->prereqv->allocreq = 1; + + /* iTemps must have a valid initial value, otherwise */ + /* downstream algorithms will have problems. If */ + /* there's a problem with the user program such that */ + /* something was left undefined, add an initializer */ + /* to the last common dominator before defs/uses. */ + /* First, find the common dominators of all defs/uses */ + unvisitBlocks(ebbs, count); + used = newBitVect(count); + for (i = 0; i < iCodeKey; i++) { + if (bitVectBitValue(OP_USES(op), i) || + bitVectBitValue(OP_DEFS(op), i)) { + bitVect *blockdomVect; + iCode *usedic; + usedic = hTabItemWithKey(iCodehTab, i); + if (!usedic) + continue; + if (ebbs[usedic->eBBlockNum]->visited) + continue; + ebbs[usedic->eBBlockNum]->visited = 1; + if (bitVectBitValue(OP_USES(op), i)) + used = bitVectSetBit(used, usedic->eBBlockNum); + blockdomVect = ebbs[usedic->eBBlockNum]->domVect; + if (dom) + dom = bitVectInplaceIntersect(dom, blockdomVect); + else + dom = bitVectCopy(blockdomVect); + } + } + /* Find the common dominator with highest block num */ + blocknum = 0; + for (i = 0; i < dom->size; i++) + if (bitVectBitValue(dom, i) && !ebbs[i]->partOfLoop) + blocknum = i; + /* If there was a use in this block, set the insertion */ + /* point near the beginning of the block, otherwise */ + /* near the end */ + if (bitVectBitValue(used, blocknum)) { + ip = ebbs[blocknum]->sch; + while (ip && + (ip->op == LABEL || ip->op == FUNCTION || ip->op == RECEIVE)) + ip = ip->next; + } else + ip = NULL; + /* Finally, create initializer and insert it*/ + val = valCastLiteral(operandType(op), 0.0, 0); + newic = newiCode('=', NULL, operandFromValue(val)); + IC_RESULT(newic) = operandFromOperand(op); + IC_RESULT(newic)->isaddr = 0; + OP_DEFS(IC_RESULT(newic)) = OP_DEFS(op) = + bitVectSetBit(OP_DEFS(op), newic->key); + addiCodeToeBBlock(ebbs[blocknum], newic, ip); + newic->eBBlockNum = blocknum; + if (!ip && newic->prev) { + newic->filename = newic->prev->filename; + newic->lineno = newic->prev->lineno; + } + ebbs[blocknum]->defSet = + bitVectSetBit(ebbs[blocknum]->defSet, newic->key); + freeBitVect(used); + freeBitVect(dom); + change++; } } else { werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, OP_SYMBOL(op)->name); + OP_SYMBOL(op)->allocreq = 1; + OP_SYMBOL(op)->addrtaken = 1; /* just to force allocation */ } } /* is this block part of a loop? */ @@ -415,6 +484,7 @@ static void findPrevUse(eBBlock *ebp, iCode *ic, operand *op, eBBlock **ebbs, markWholeLoop(ebp, op->key); } } + return change; } /*-----------------------------------------------------------------*/ @@ -449,11 +519,15 @@ static void rliveClear(eBBlock **ebbs, int count) { /* rlivePoint - for each point compute the ranges that are alive */ /* The live range is only stored for ITEMPs; the same code is used */ /* to find use of unitialized AUTOSYMs (an ITEMP is an AUTOSYM). */ +/* also, update funcUsesVolatile flag for current function */ /*-----------------------------------------------------------------*/ -static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { +static int rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { int i, key; eBBlock *succ; bitVect *alive; + int change = 0; + + bool uses_volatile = false; /* for all blocks do */ for (i = 0; i < count; i++) { @@ -461,20 +535,28 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { /* for all instructions in this block do */ for (ic = ebbs[i]->sch; ic; ic = ic->next) { + uses_volatile |= + POINTER_GET(ic) && IS_VOLATILE(operandType(IC_LEFT(ic))->next) || + IS_OP_VOLATILE(IC_LEFT(ic)) || IS_OP_VOLATILE(IC_RIGHT(ic)); + uses_volatile |= + POINTER_SET(ic) && IS_VOLATILE(operandType(IC_RESULT(ic))->next) || + IS_OP_VOLATILE(IC_RESULT(ic)); if (!ic->rlive) ic->rlive = newBitVect(operandKey); if (SKIP_IC2(ic)) continue; - + if (ebbs[i]->noPath) + continue; if (ic->op == JUMPTABLE && IS_SYMOP(IC_JTCOND(ic))) { incUsed(ic, IC_JTCOND(ic)); if (!IS_AUTOSYM(IC_JTCOND(ic))) continue; - findPrevUse(ebbs[i], ic, IC_JTCOND(ic), ebbs, count, emitWarnings); + change += + findPrevUse(ebbs[i], ic, IC_JTCOND(ic), ebbs, count, emitWarnings); if (IS_ITEMP(IC_JTCOND(ic))) { unvisitBlocks(ebbs, count); ic->rlive = bitVectSetBit(ic->rlive, IC_JTCOND(ic)->key); @@ -490,7 +572,8 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { if (!IS_AUTOSYM(IC_COND(ic))) continue; - findPrevUse(ebbs[i], ic, IC_COND(ic), ebbs, count, emitWarnings); + change += + findPrevUse(ebbs[i], ic, IC_COND(ic), ebbs, count, emitWarnings); if (IS_ITEMP(IC_COND(ic))) { unvisitBlocks(ebbs, count); ic->rlive = bitVectSetBit(ic->rlive, IC_COND(ic)->key); @@ -503,7 +586,8 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { if (IS_SYMOP(IC_LEFT(ic))) { incUsed(ic, IC_LEFT(ic)); if (IS_AUTOSYM(IC_LEFT(ic)) && ic->op != ADDRESS_OF) { - findPrevUse(ebbs[i], ic, IC_LEFT(ic), ebbs, count, emitWarnings); + change += + findPrevUse(ebbs[i], ic, IC_LEFT(ic), ebbs, count, emitWarnings); if (IS_ITEMP(IC_LEFT(ic))) { unvisitBlocks(ebbs, count); ic->rlive = bitVectSetBit(ic->rlive, IC_LEFT(ic)->key); @@ -526,7 +610,8 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { if (IS_SYMOP(IC_RIGHT(ic))) { incUsed(ic, IC_RIGHT(ic)); if (IS_AUTOSYM(IC_RIGHT(ic))) { - findPrevUse(ebbs[i], ic, IC_RIGHT(ic), ebbs, count, emitWarnings); + change += + findPrevUse(ebbs[i], ic, IC_RIGHT(ic), ebbs, count, emitWarnings); if (IS_ITEMP(IC_RIGHT(ic))) { unvisitBlocks(ebbs, count); ic->rlive = bitVectSetBit(ic->rlive, IC_RIGHT(ic)->key); @@ -540,7 +625,8 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { if (IS_AUTOSYM(IC_RESULT(ic))) { if (POINTER_SET(ic)) { - findPrevUse(ebbs[i], ic, IC_RESULT(ic), ebbs, count, emitWarnings); + change += findPrevUse(ebbs[i], ic, IC_RESULT(ic), ebbs, count, + emitWarnings); } if (IS_ITEMP(IC_RESULT(ic))) { unvisitBlocks(ebbs, count); @@ -582,6 +668,10 @@ static void rlivePoint(eBBlock **ebbs, int count, bool emitWarnings) { findNextUseSym(ebbs[i], NULL, hTabItemWithKey(liveRanges, key)); } } + + if (currFunc) + currFunc->funcUsesVolatile = uses_volatile; + return change; } /*-----------------------------------------------------------------*/ @@ -730,6 +820,7 @@ void adjustIChain(eBBlock **ebbs, int count) { /* computeLiveRanges - computes the live ranges for variables */ /*-----------------------------------------------------------------*/ void computeLiveRanges(eBBlock **ebbs, int count, bool emitWarnings) { + int change; /* first look through all blocks and adjust the sch and ech pointers */ adjustIChain(ebbs, count); @@ -737,17 +828,20 @@ void computeLiveRanges(eBBlock **ebbs, int count, bool emitWarnings) { /* sequence the code the live ranges are computed in terms of this sequence additionally the routine will also create a hashtable of instructions */ - iCodeSeq = 0; - setToNull((void *)&iCodehTab); - iCodehTab = newHashTable(iCodeKey); - hashiCodeKeys(ebbs, count); - setToNull((void *)&iCodeSeqhTab); - iCodeSeqhTab = newHashTable(iCodeKey); - sequenceiCode(ebbs, count); - - /* mark the ranges live for each point */ - setToNull((void *)&liveRanges); - rlivePoint(ebbs, count, emitWarnings); + do { + iCodeSeq = 0; + setToNull((void *)&iCodehTab); + iCodehTab = newHashTable(iCodeKey); + hashiCodeKeys(ebbs, count); + setToNull((void *)&iCodeSeqhTab); + iCodeSeqhTab = newHashTable(iCodeKey); + sequenceiCode(ebbs, count); + + /* mark the ranges live for each point */ + setToNull((void *)&liveRanges); + change = rlivePoint(ebbs, count, emitWarnings); + emitWarnings = FALSE; + } while (change); /* mark the from & to live ranges for variables used */ markLiveRanges(ebbs, count); @@ -819,6 +913,9 @@ dumpIcRlive (eBBlock ** ebbs, int count) } #endif +/*-----------------------------------------------------------------*/ +/* Visit all iCodes reachable from ic */ +/*-----------------------------------------------------------------*/ static void visit(set **visited, iCode *ic, const int key) { symbol *lbl; @@ -860,14 +957,14 @@ static void visit(set **visited, iCode *ic, const int key) { /* Such temporaries can result from GCSE and losrpe, */ /* And can confuse register allocation and rematerialization. */ /*-----------------------------------------------------------------*/ -void separateLiveRanges(iCode *sic, ebbIndex *ebbi) { - iCode *ic; +int separateLiveRanges(iCode *sic, ebbIndex *ebbi) { set *candidates = 0; symbol *sym; + int num_separated = 0; // printf("separateLiveRanges()\n"); - for (ic = sic; ic; ic = ic->next) { + for (iCode *ic = sic; ic; ic = ic->next) { if (ic->op == IFX || ic->op == GOTO || ic->op == JUMPTABLE || !IC_RESULT(ic) || !IS_ITEMP(IC_RESULT(ic)) || bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) <= 1 || @@ -878,26 +975,36 @@ void separateLiveRanges(iCode *sic, ebbIndex *ebbi) { } if (!candidates) - return; + return (0); for (sym = setFirstItem(candidates); sym; sym = setNextItem(candidates)) { // printf("Looking at %s, %d definitions\n", sym->name, bitVectnBitsOn // (sym->defs)); - int i; set *defs = 0; + set *uses = 0; + bool skip_uses = false; - for (i = 0; i < sym->defs->size; i++) + for (int i = 0; i < sym->defs->size; i++) { if (bitVectBitValue(sym->defs, i)) { iCode *dic; if (dic = hTabItemWithKey(iCodehTab, i)) - addSet(&defs, hTabItemWithKey(iCodehTab, i)); + addSet(&defs, dic); else { werror(W_INTERNAL_ERROR, __FILE__, __LINE__, "Definition not found"); - return; + return (num_separated); } } - + if (bitVectBitValue(sym->uses, i)) { + iCode *uic; + if (uic = hTabItemWithKey(iCodehTab, i)) + addSet(&uses, uic); + else + skip_uses = + true; // werror (W_INTERNAL_ERROR, __FILE__, __LINE__, "Use not + // found"); // return (num_separated); seems too harsh. + } + } do { set *visited = 0; set *newdefs = 0; @@ -920,8 +1027,8 @@ void separateLiveRanges(iCode *sic, ebbIndex *ebbi) { do { oldsize = elementsInSet(visited); - ic = setFirstItem(defs); - for (ic = setNextItem(defs); ic; ic = setNextItem(defs)) { + setFirstItem(defs); + for (iCode *ic = setNextItem(defs); ic; ic = setNextItem(defs)) { // printf("Looking at other def at %d now\n", ic->key); set *visited2 = 0; set *intersection = 0; @@ -947,7 +1054,7 @@ void separateLiveRanges(iCode *sic, ebbIndex *ebbi) { // OP_SYMBOL_CONST(tmpop)->name, sym->name, ((iCode *)(setFirstItem // (newdefs)))->key, ((iCode *)(setFirstItem (newdefs)))->op); - for (ic = setFirstItem(visited); ic; ic = setNextItem(visited)) { + for (iCode *ic = setFirstItem(visited); ic; ic = setNextItem(visited)) { if (IC_LEFT(ic) && IS_ITEMP(IC_LEFT(ic)) && OP_SYMBOL(IC_LEFT(ic)) == sym) IC_LEFT(ic) = operandFromOperand(tmpop); @@ -964,16 +1071,145 @@ void separateLiveRanges(iCode *sic, ebbIndex *ebbi) { IC_RESULT(ic) = operandFromOperand(tmpop); if (pset) IC_RESULT(ic)->isaddr = TRUE; + else + bitVectUnSetBit(sym->defs, ic->key); + } + bitVectUnSetBit(sym->uses, ic->key); + + skip_uses = true; + num_separated++; + } + } else if (!skip_uses) { + set *undefined_uses = 0; + undefined_uses = subtractFromSet(uses, visited, THROW_NONE); + + // Eliminate uses of undefined variables. + for (iCode *ic = setFirstItem(undefined_uses); ic; + ic = setNextItem(undefined_uses)) { + iCode *prev = ic->prev; + iCode *next = ic->next; + if (prev && next) { + prev->next = next; + next->prev = prev; } + bitVectUnSetBit(sym->uses, ic->key); + if (IS_SYMOP(IC_RESULT(ic))) + bitVectUnSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); } + + deleteSet(&undefined_uses); } + deleteSet(&newdefs); deleteSet(&visited); } while (elementsInSet(defs) > 1); deleteSet(&defs); + deleteSet(&uses); } deleteSet(&candidates); + + return (num_separated); +} + +/*-----------------------------------------------------------------*/ +/* Shorten live ranges by swapping order of operations */ +/*-----------------------------------------------------------------*/ +int shortenLiveRanges(iCode *sic, ebbIndex *ebbi) { + int change = 0; + + for (iCode *ic = sic; ic; ic = ic->next) { + iCode *ifx = 0; + + iCode *pic = ic->prev; + iCode *nic = ic->next; + + if (!pic || !nic) + continue; + + if (ic->op == IFX || nic->op == IFX) + continue; + + if (nic->op == IPUSH || nic->op == SEND || nic->op == RETURN) + continue; + + if (pic->op != '=' || !IS_ITEMP(IC_RESULT(pic)) || + bitVectnBitsOn(OP_DEFS(IC_RESULT(pic))) != 1) + continue; + + if (IC_LEFT(nic) != IC_RESULT(pic) && IC_RIGHT(nic) != IC_RESULT(pic) || + bitVectnBitsOn(OP_USES(IC_RESULT(pic))) != 1) + continue; + + if (IS_OP_VOLATILE(IC_RIGHT(pic)) || IS_OP_VOLATILE(IC_LEFT(nic)) || + IS_OP_VOLATILE(IC_RIGHT(nic)) || IS_OP_VOLATILE(IC_RESULT(nic))) + continue; + + if (isOperandEqual(IC_RESULT(pic), IC_LEFT(ic)) || + isOperandEqual(IC_RESULT(pic), IC_RIGHT(ic))) + continue; + + if (isOperandEqual(IC_RESULT(ic), IC_LEFT(nic)) || + isOperandEqual(IC_RESULT(ic), IC_RIGHT(nic))) + continue; + + if ((POINTER_SET(nic) || isOperandGlobal(IC_RESULT(nic))) && + (POINTER_GET(ic) || isOperandGlobal(IC_LEFT(ic)) || + isOperandGlobal(IC_RIGHT(ic))) || + (POINTER_GET(nic) || isOperandGlobal(IC_LEFT(nic)) || + isOperandGlobal(IC_RIGHT(nic))) && + (POINTER_SET(ic) || + POINTER_SET(nic) && isOperandGlobal(IC_RESULT(ic)))) + continue; + + if (isOperandGlobal(IC_RIGHT(pic))) + continue; + + if (ifx = ifxForOp(IC_RESULT(nic), nic)) { + const symbol *starget = IC_TRUE(ifx) ? IC_TRUE(ifx) : IC_FALSE(ifx); + const iCode *itarget = eBBWithEntryLabel(ebbi, starget)->sch; + + if (nic->next != ifx || + bitVectBitValue(itarget->rlive, IC_RESULT(ic)->key)) + continue; + } + + if (IC_LEFT(nic) == IC_RESULT(pic)) + IC_LEFT(nic) = IC_RIGHT(pic); + if (IC_RIGHT(nic) == IC_RESULT(pic)) + IC_RIGHT(nic) = IC_RIGHT(pic); + bitVectUnSetBit(OP_USES(IC_RESULT(pic)), nic->key); + if (IS_SYMOP(IC_RIGHT(pic))) + bitVectSetBit(OP_USES(IC_RIGHT(pic)), nic->key); + + // Assignment to self will get optimized out later + IC_LEFT(pic) = IC_RESULT(pic); + bitVectSetBit(OP_USES(IC_RESULT(pic)), pic->key); + + pic->next = nic; + nic->prev = pic; + ic->prev = nic; + ic->next = nic->next; + nic->next = ic; + if (ic->next) + ic->next->prev = ic; + + if (ifx) // Move calculation beyond ifx. + { + ifx->prev = ic->prev; + ic->next = ifx->next; + ifx->next = ic; + ic->prev = ifx; + + ifx->prev->next = ifx; + if (ic->next) + ic->next->prev = ic; + } + + change++; + } + + return (change); } diff --git a/src/SDCClrange.h b/src/SDCClrange.h index d67799785..a9dae2662 100644 --- a/src/SDCClrange.h +++ b/src/SDCClrange.h @@ -40,8 +40,12 @@ void hashiCodeKeys(eBBlock **, int); void adjustIChain(eBBlock **ebbs, int count); -void separateLiveRanges( +int separateLiveRanges( iCode *sic, ebbIndex *ebbi); /* Split iTemps that have non-connected live-ranges. */ +int shortenLiveRanges( + iCode *sic, + ebbIndex *ebbi); /* Do some optimizations that shorten live ranges. */ + #endif diff --git a/src/SDCCmain.c b/src/SDCCmain.c index bd83a2acc..da5e4f435 100644 --- a/src/SDCCmain.c +++ b/src/SDCCmain.c @@ -42,10 +42,14 @@ #ifdef _WIN32 #include #else +#include #include #include #endif +/* REMOVE ME!!! */ +extern int yyparse(void); + FILE *srcFile; /* source file */ const char *fullSrcFileName; /* full name for the source file; */ /* can be NULL while c1mode or linking without compiling */ @@ -90,10 +94,6 @@ char buffer[PATH_MAX * 2]; #define OPTION_HELP "--help" #define OPTION_OUT_FMT_IHX "--out-fmt-ihx" #define OPTION_OUT_FMT_S19 "--out-fmt-s19" -#define OPTION_HUGE_MODEL "--model-huge" -#define OPTION_LARGE_MODEL "--model-large" -#define OPTION_MEDIUM_MODEL "--model-medium" -#define OPTION_SMALL_MODEL "--model-small" #define OPTION_PEEP_FILE "--peep-file" #define OPTION_LIB_PATH "--lib-path" #define OPTION_CALLEE_SAVES "--callee-saves" @@ -115,7 +115,6 @@ char buffer[PATH_MAX * 2]; #define OPTION_WERROR "--Werror" #define OPTION_DEBUG "--debug" #define OPTION_NO_GCSE "--nogcse" -#define OPTION_SHORT_IS_8BITS "--short-is-8bits" #define OPTION_NO_XINIT_OPT "--no-xinit-opt" #define OPTION_ICODE_IN_ASM "--i-code-in-asm" #define OPTION_PRINT_SEARCH_DIRS "--print-search-dirs" @@ -126,27 +125,37 @@ char buffer[PATH_MAX * 2]; #define OPTION_OPT_CODE_SPEED "--opt-code-speed" #define OPTION_OPT_CODE_SIZE "--opt-code-size" #define OPTION_STD_C89 "--std-c89" +#define OPTION_STD_C95 "--std-c95" #define OPTION_STD_C99 "--std-c99" #define OPTION_STD_C11 "--std-c11" +#define OPTION_STD_C2X "--std-c2x" #define OPTION_STD_SDCC89 "--std-sdcc89" #define OPTION_STD_SDCC99 "--std-sdcc99" +#define OPTION_STD_SDCC11 "--std-sdcc11" +#define OPTION_STD_SDCC2X "--std-sdcc2x" #define OPTION_CODE_SEG "--codeseg" #define OPTION_CONST_SEG "--constseg" +#define OPTION_DATA_SEG "--dataseg" #define OPTION_DOLLARS_IN_IDENT "--fdollars-in-identifiers" -#define OPTION_UNSIGNED_CHAR "--funsigned-char" +#define OPTION_SIGNED_CHAR "--fsigned-char" #define OPTION_USE_NON_FREE "--use-non-free" #define OPTION_PEEP_RETURN "--peep-return" #define OPTION_NO_PEEP_RETURN "--no-peep-return" #define OPTION_NO_OPTSDCC_IN_ASM "--no-optsdcc-in-asm" #define OPTION_MAX_ALLOCS_PER_NODE "--max-allocs-per-node" #define OPTION_NO_LOSPRE "--nolospre" -#define OPTION_LOSPRE_UNSAFE_READ "--lospre-unsafe-read" +#define OPTION_ALLOW_UNSAFE_READ "--allow-unsafe-read" #define OPTION_DUMP_AST "--dump-ast" #define OPTION_DUMP_I_CODE "--dump-i-code" #define OPTION_DUMP_GRAPHS "--dump-graphs" #define OPTION_CPP "--cpp" #define OPTION_NO_CLEANUP "--no-cleanup" +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_MEDIUM_MODEL "--model-medium" +#define OPTION_LARGE_MODEL "--model-large" +#define OPTION_HUGE_MODEL "--model-huge" + static const OPTION optionsTable[] = { {0, NULL, NULL, "General options"}, {0, OPTION_HELP, NULL, "Display this help"}, @@ -155,11 +164,12 @@ static const OPTION optionsTable[] = { "Trace calls to the preprocessor, assembler, and linker"}, {'V', NULL, &options.verboseExec, "Execute verbosely. Show sub commands as they are run"}, - {'d', NULL, NULL, NULL}, + {'d', NULL, NULL, + "Output list of macro definitions in effect. Use with -E"}, {'D', NULL, NULL, "Define macro as in -Dmacro"}, {'I', NULL, NULL, "Add to the include (*.h) path, as in -Ipath"}, {'A', NULL, NULL, NULL}, - {'U', NULL, NULL, NULL}, + {'U', NULL, NULL, "Undefine macro as in -Umacro"}, {'M', NULL, NULL, "Preprocessor option"}, {'W', NULL, NULL, "Pass through options to the pre-processor (p), assembler (a) or linker " @@ -177,6 +187,10 @@ static const OPTION optionsTable[] = { {0, OPTION_MSVC_ERROR_STYLE, &options.vc_err_style, "messages are compatible with Micro$oft visual studio"}, {0, OPTION_USE_STDOUT, NULL, "send errors to stdout instead of stderr"}, + {0, "--nostdlib", &options.nostdlib, + "Do not include the standard library directory in the search path"}, + {0, "--nostdinc", &options.nostdinc, + "Do not include the standard include directory in the search path"}, {0, OPTION_LESS_PEDANTIC, NULL, "Disable some of the more pedantic warnings"}, {0, OPTION_DISABLE_WARNING, NULL, " Disable specific warning"}, @@ -185,16 +199,23 @@ static const OPTION optionsTable[] = { {0, "--cyclomatic", &options.cyclomatic, "Display complexity of compiled functions"}, {0, "--std", NULL, "Use the specified C standard"}, - {0, OPTION_STD_C89, NULL, "Use C89 standard (slightly incomplete)"}, + {0, OPTION_STD_C89, NULL, + "Use ISO C90 (aka ANSI C89) standard (slightly incomplete)"}, {0, OPTION_STD_SDCC89, NULL, - "Use C89 standard with SDCC extensions (default)"}, - {0, OPTION_STD_C99, NULL, "Use C99 standard (incomplete)"}, - {0, OPTION_STD_SDCC99, NULL, "Use C99 standard with SDCC extensions"}, - {0, OPTION_STD_C11, NULL, "Use C11 standard (very incomplete)"}, + "Use ISO C90 (aka ANSI C89) standard with SDCC extensions"}, + {0, OPTION_STD_C95, NULL, + "Use ISO C95 (aka ISO C94) standard (slightly incomplete)"}, + {0, OPTION_STD_C99, NULL, "Use ISO C99 standard (incomplete)"}, + {0, OPTION_STD_SDCC99, NULL, "Use ISO C99 standard with SDCC extensions"}, + {0, OPTION_STD_C11, NULL, "Use ISO C11 standard (incomplete)"}, + {0, OPTION_STD_SDCC11, NULL, + "Use ISO C11 standard with SDCC extensions (default)"}, + {0, OPTION_STD_C2X, NULL, "Use ISO C2X standard (incomplete)"}, + {0, OPTION_STD_SDCC2X, NULL, "Use ISO C2X standard with SDCC extensions"}, {0, OPTION_DOLLARS_IN_IDENT, &options.dollars_in_ident, "Permit '$' as an identifier character"}, - {0, OPTION_UNSIGNED_CHAR, &options.unsigned_char, - "Make \"char\" unsigned by default"}, + {0, OPTION_SIGNED_CHAR, &options.signed_char, + "Make \"char\" signed by default"}, {0, NULL, NULL, "Code generation options"}, {0, "--stack-auto", &options.stackAuto, "Stack automatic variables"}, @@ -221,9 +242,9 @@ static const OPTION optionsTable[] = { "don't memcpy initialized xram from code"}, {0, OPTION_NO_PEEP_COMMENTS, &options.noPeepComments, "don't include peephole optimizer comments"}, - {0, OPTION_SHORT_IS_8BITS, NULL, "Make short 8 bits (for old times sake)"}, {0, OPTION_CODE_SEG, NULL, " use this name for the code segment"}, {0, OPTION_CONST_SEG, NULL, " use this name for the const segment"}, + {0, OPTION_DATA_SEG, NULL, " use this name for the data segment"}, {0, NULL, NULL, "Optimization options"}, {0, "--nooverlay", &options.noOverlay, @@ -232,8 +253,6 @@ static const OPTION optionsTable[] = { {0, OPTION_NO_LABEL_OPT, NULL, "Disable label optimisation"}, {0, OPTION_NO_LOOP_INV, NULL, "Disable optimisation of invariants"}, {0, OPTION_NO_LOOP_IND, NULL, "Disable loop variable induction"}, - {0, "--nojtbound", &optimize.noJTabBoundary, - "Don't generate boundary check for jump tables"}, {0, "--noloopreverse", &optimize.noLoopReverse, "Disable the loop reverse optimisation"}, {0, "--no-peep", &options.nopeep, @@ -256,7 +275,10 @@ static const OPTION optionsTable[] = { "tree decomposition", CLAT_INTEGER}, {0, OPTION_NO_LOSPRE, NULL, "Disable lospre"}, - {0, OPTION_LOSPRE_UNSAFE_READ, NULL, "Allow unsafe reads in lospre"}, + {0, OPTION_ALLOW_UNSAFE_READ, NULL, + "Allow optimizations to read any memory location anytime"}, + {0, "--nostdlibcall", &optimize.noStdLibCall, + "Disable optimization of calls to standard library"}, {0, NULL, NULL, "Internal debugging options"}, {0, OPTION_DUMP_AST, &options.dump_ast, @@ -395,6 +417,16 @@ void setParseWithComma(set **dest, const char *src) { } } +/*-------------------------------------------------------------*/ +/* setStackSize - set the stack size of a running sdcc process */ +/*-------------------------------------------------------------*/ +static void setStackSize(void) { +#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK) + struct rlimit rl = {4 * 1024 * 1024, 4 * 1024 * 1024}; + setrlimit(RLIMIT_STACK, &rl); +#endif +} + /*-----------------------------------------------------------------*/ /* setDefaultOptions - sets the default options */ /*-----------------------------------------------------------------*/ @@ -408,22 +440,30 @@ static void setDefaultOptions(void) { 1; /* MB: Do not use address 0 by default as it equals NULL */ options.idata_loc = 0; /* MB: No need to limit idata to 0x80-0xFF */ options.nopeep = 0; + options.model = port->general.default_model; options.nostdlib = 0; options.nostdinc = 0; options.verbose = 0; - options.shortis8bits = 0; options.std_sdcc = 0; /* enable SDCC language extensions */ - options.std_c99 = 0; /* default to C89 until more C99 support */ - options.std_c11 = 0; /* default to C89 until more C11 support */ + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = + 1; /* default to C11 (we want inline by default, so we need at least C99, + and support for C11 is more complete than C99) */ + options.std_c2x = 0; options.code_seg = CODE_NAME ? Safe_strdup(CODE_NAME) : NULL; /* default to CSEG for generated code */ options.const_seg = CONST_NAME ? Safe_strdup(CONST_NAME) : NULL; /* default to CONST for generated code */ + options.data_seg = DATA_NAME + ? Safe_strdup(DATA_NAME) + : NULL; /* default to DATA for non-initialized data */ options.stack10bit = 0; options.out_fmt = 0; options.dump_graphs = 0; options.preprocessor = NULL; + options.dependencyFileOpt = 0; /* now for the optimizations */ /* turn on the everything */ @@ -436,7 +476,7 @@ static void setDefaultOptions(void) { optimize.loopInduction = 1; options.max_allocs_per_node = 3000; optimize.lospre = 1; - optimize.lospre_unsafe_read = 0; + optimize.allow_unsafe_read = 0; /* now for the ports */ port->setDefaultOptions(); @@ -455,9 +495,9 @@ static void processFile(char *s) { /* get the file extension. If no '.' then we don't know what the file type is - so give a warning and return */ + so give an error and return */ if (!dbuf_splitFile(s, &path, &ext)) { - werror(W_UNKNOWN_FEXT, s); + werror(E_UNKNOWN_FEXT, s); dbuf_destroy(&ext); dbuf_destroy(&path); @@ -467,7 +507,7 @@ static void processFile(char *s) { /* otherwise depending on the file type */ extp = dbuf_c_str(&ext); - if (STRCASECMP(extp, ".c") == 0) { + if (STRCASECMP(extp, ".c") == 0 || STRCASECMP(extp, ".h") == 0) { char *p, *m; dbuf_destroy(&ext); @@ -485,7 +525,7 @@ static void processFile(char *s) { /* the only source file */ fullSrcFileName = s; if (!(srcFile = fopen(fullSrcFileName, "r"))) { - werror(E_FILE_OPEN_ERR, s); + werror(E_INPUT_FILE_OPEN_ERR, fullSrcFileName, strerror(errno)); dbuf_destroy(&path); @@ -531,7 +571,14 @@ static void processFile(char *s) { dbuf_destroy(&ext); dbuf_destroy(&path); - werror(W_UNKNOWN_FEXT, s); + werror(E_UNKNOWN_FEXT, s); +} + +static void _setModel(int model, const char *sz) { + if (port->general.supported_models & model) + options.model = model; + else + werror(W_UNSUPPORTED_MODEL, sz, port->target); } /** Gets the string argument to this option. If the option is '--opt' @@ -555,7 +602,7 @@ char *getStringArg(const char *szStart, char **argv, int *pi, int argc) { /** Gets the integer argument to this option using the same rules as getStringArg. */ -int getIntArg(const char *szStart, char **argv, int *pi, int argc) { +long getIntArg(const char *szStart, char **argv, int *pi, int argc) { char *p; int val; char *str = getStringArg(szStart, argv, pi, argc); @@ -716,6 +763,8 @@ static int parseCmdLine(int argc, char **argv) { /* go thru all whole command line */ for (i = 1; i < argc; i++) { + if (i >= argc) + break; /* check port specific options before general ones */ if (port->parseOption(&argc, argv, &i) == TRUE) { @@ -811,8 +860,8 @@ static int parseCmdLine(int argc, char **argv) { continue; } - if (strcmp(argv[i], OPTION_LOSPRE_UNSAFE_READ) == 0) { - optimize.lospre_unsafe_read = 1; + if (strcmp(argv[i], OPTION_ALLOW_UNSAFE_READ) == 0) { + optimize.allow_unsafe_read = 1; continue; } @@ -834,45 +883,92 @@ static int parseCmdLine(int argc, char **argv) { continue; } - if (strcmp(argv[i], OPTION_SHORT_IS_8BITS) == 0) { - printf("Option %s is deprecated and will be removed in the future.\n", - OPTION_SHORT_IS_8BITS); - options.shortis8bits = 1; + if (strcmp(argv[i], OPTION_STD_C89) == 0 || + strcmp(argv[i], "--std=c89") == 0) { + options.std_c95 = 0; + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; continue; } - if (strcmp(argv[i], OPTION_STD_C89) == 0 || - !strcmp(argv[i], "--std=c89")) { + if (strcmp(argv[i], OPTION_STD_C95) == 0 || + strcmp(argv[i], "--std=c95") == 0) { + options.std_c95 = 1; options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + continue; + } + + if (strcmp(argv[i], OPTION_STD_C99) == 0 || + strcmp(argv[i], "--std=c99") == 0) { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 0; continue; } if (strcmp(argv[i], OPTION_STD_C99) == 0 || !strcmp(argv[i], "--std=c99")) { + options.std_c95 = 1; options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 0; options.std_sdcc = 0; continue; } - if (strcmp(argv[i], OPTION_STD_C11) == 0 || - !strcmp(argv[i], "--std=c11")) { + if (strcmp(argv[i], OPTION_STD_C2X) == 0 || + strcmp(argv[i], "--std=c2x") == 0) { + options.std_c95 = 1; options.std_c99 = 1; options.std_c11 = 1; + options.std_c2x = 1; options.std_sdcc = 0; continue; } if (strcmp(argv[i], OPTION_STD_SDCC89) == 0 || !strcmp(argv[i], "--std=sdcc89")) { + options.std_c95 = 0; options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; options.std_sdcc = 1; continue; } if (strcmp(argv[i], OPTION_STD_SDCC99) == 0 || - !strcmp(argv[i], "--std=sdcc99")) { + strcmp(argv[i], "--std=sdcc99")) { + options.std_c95 = 1; options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 1; + continue; + } + + if (strcmp(argv[i], OPTION_STD_SDCC11) == 0 || + strcmp(argv[i], "--std=sdcc11")) { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 0; + options.std_sdcc = 1; + continue; + } + + if (strcmp(argv[i], OPTION_STD_SDCC2X) == 0 || + !strcmp(argv[i], "--std=sdcc2x")) { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 1; options.std_sdcc = 1; continue; } @@ -912,6 +1008,18 @@ static int parseCmdLine(int argc, char **argv) { continue; } + if (strcmp(argv[i], OPTION_DATA_SEG) == 0) { + struct dbuf_s segname; + + dbuf_init(&segname, 16); + dbuf_printf(&segname, "%-8s(DATA)", + getStringArg(OPTION_DATA_SEG, argv, &i, argc)); + if (options.data_seg) + Safe_free(options.data_seg); + options.data_seg = dbuf_detach(&segname); + continue; + } + if (strcmp(argv[i], OPTION_PEEP_RETURN) == 0) { options.peepReturn = 1; continue; @@ -1024,11 +1132,21 @@ static int parseCmdLine(int argc, char **argv) { /* preprocessor options */ case 'M': { - preProcOnly = 1; - if (argv[i][2] == 'M') - addSet(&preArgvSet, Safe_strdup("-MM")); - else - addSet(&preArgvSet, Safe_strdup("-M")); + if (argv[i][2] == 'M') { + if (argv[i][3] == 'D') { + options.dependencyFileOpt = USER_DEPENDENCY_FILE_OPT; + } else { + addSet(&preArgvSet, Safe_strdup("-MM")); + preProcOnly = 1; + } + } else { + if (argv[i][2] == 'D') { + options.dependencyFileOpt = SYSTEM_DEPENDENCY_FILE_OPT; + } else { + addSet(&preArgvSet, Safe_strdup("-M")); + preProcOnly = 1; + } + } break; } @@ -1305,6 +1423,29 @@ static int preProcess(char **envp) { set *inclList = NULL; char *buf; + if (options.dependencyFileOpt) { + struct dbuf_s dbuf; + + dbuf_init(&dbuf, PATH_MAX); + if (options.dependencyFileOpt == SYSTEM_DEPENDENCY_FILE_OPT) + dbuf_append_str(&dbuf, "-MD "); + else + dbuf_append_str(&dbuf, "-MMD "); + if (fullDstFileName) + dbuf_splitFile(fullDstFileName, &dbuf, NULL); + else + dbuf_append_str(&dbuf, dstFileName); + dbuf_append_str(&dbuf, ".d"); + addSet(&preArgvSet, dbuf_detach_c_str(&dbuf)); + + dbuf_init(&dbuf, PATH_MAX); + if (fullDstFileName) + dbuf_printf(&dbuf, "-MT %s", fullDstFileName); + else + dbuf_printf(&dbuf, "-MT %s%s", dstFileName, port->linker.rel_ext); + addSet(&preArgvSet, dbuf_detach_c_str(&dbuf)); + } + /* if using dollar signs in identifiers */ if (options.dollars_in_ident) addSet(&preArgvSet, Safe_strdup("-fdollars-in-identifiers")); @@ -1312,26 +1453,26 @@ static int preProcess(char **envp) { /* set the macro for no overlay */ if (options.noOverlay) addSet(&preArgvSet, Safe_strdup("-D__SDCC_NOOVERLAY")); - if (options.std_sdcc && options.noOverlay) - addSet(&preArgvSet, Safe_strdup("-DSDCC_NOOVERLAY")); /* set the macro for unsigned char */ - if (options.unsigned_char) + if (options.signed_char) + addSet(&preArgvSet, Safe_strdup("-D__SDCC_CHAR_SIGNED")); + else addSet(&preArgvSet, Safe_strdup("-D__SDCC_CHAR_UNSIGNED")); - if (options.std_sdcc && options.unsigned_char) - addSet(&preArgvSet, Safe_strdup("-DSDCC_CHAR_UNSIGNED")); + + /* set macro for optimization level */ + if (optimize.codeSpeed) + addSet(&preArgvSet, Safe_strdup("-D__SDCC_OPTIMIZE_SPEED")); + if (optimize.codeSize) + addSet(&preArgvSet, Safe_strdup("-D__SDCC_OPTIMIZE_SIZE")); /* set macro corresponding to compiler option */ if (options.intlong_rent) addSet(&preArgvSet, Safe_strdup("-D__SDCC_INT_LONG_REENT")); - if (options.std_sdcc && options.intlong_rent) - addSet(&preArgvSet, Safe_strdup("-DSDCC_INT_LONG_REENT")); /* set macro corresponding to compiler option */ if (options.float_rent) addSet(&preArgvSet, Safe_strdup("-D__SDCC_FLOAT_REENT")); - if (options.std_sdcc && options.float_rent) - addSet(&preArgvSet, Safe_strdup("-DSDCC_FLOAT_REENT")); if (options.all_callee_saves) addSet(&preArgvSet, Safe_strdup("-D__SDCC_ALL_CALLEE_SAVES")); @@ -1349,23 +1490,32 @@ static int preProcess(char **envp) { struct dbuf_s dbuf; dbuf_init(&dbuf, 20); - dbuf_printf(&dbuf, "-DKCC=%d%d%d", KCC_VERSION_HI, KCC_VERSION_LO, - KCC_VERSION_P); addSet(&preArgvSet, dbuf_detach_c_str(&dbuf)); } /* add port (processor information to processor */ addSet(&preArgvSet, Safe_strdup("-D__SDCC_{port}")); - if (options.std_sdcc) { - addSet(&preArgvSet, Safe_strdup("-DSDCC_{port}")); - addSet(&preArgvSet, Safe_strdup("-D__{port}")); - } - /* Optinal C features not (yet) supported by sdcc */ - addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_COMPLEX__")); - addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_THREADS__")); - addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_ATOMICS__")); - addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_VLA__")); + /* Optional C features not (yet) supported by SDCC */ + addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_COMPLEX__=1")); + addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_THREADS__=1")); + addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_ATOMICS__=1")); + addSet(&preArgvSet, Safe_strdup("-D__STDC_NO_VLA__=1")); + + /* Character encoding - these need to be set in device/lib/Makefile.in for + * $CPP, too */ + addSet(&preArgvSet, + Safe_strdup("-D__STDC_ISO_10646__=201409L")); // wchar_t is UTF-32 + addSet(&preArgvSet, + Safe_strdup("-D__STDC_UTF_16__=1")); // char16_t is UTF-16 + addSet(&preArgvSet, + Safe_strdup("-D__STDC_UTF_32__=1")); // char32_t is UTF-32 + + /* standard include path */ + if (!options.nostdinc) { + inclList = processStrSet(includeDirsSet, "-isystem ", NULL, shell_escape); + mergeSets(&preArgvSet, inclList); + } setMainValue("cppextraopts", (s = joinStrSet(preArgvSet))); Safe_free((void *)s); @@ -1393,6 +1543,7 @@ static int preProcess(char **envp) { if (options.verbose) printf("kcc: Calling preprocessor...\n"); buf = buildMacros(_preCmd); + buf = setPrefixSuffix(buf); if (preProcOnly) { if (sdcc_system(buf)) { @@ -1602,10 +1753,12 @@ static void initValues(void) { * Make sure the preprocessor is called with the "-std" option * corresponding to the --std used to start sdcc */ - if (!options.std_sdcc) - setMainValue("cppstd", options.std_c11 - ? "-std=c11 " - : (options.std_c99 ? "-std=c99 " : "-std=c89 ")); + setMainValue("cppstd", options.std_c11 + ? "-std=c11 " + : (options.std_c99 + ? "-std=c99 " + : (options.std_c95 ? "-std=iso9899:199409 " + : "-std=c89 "))); } static void doPrintSearchDirs(void) { @@ -1662,6 +1815,12 @@ static void sig_handler(int signal) { */ int main(int argc, char **argv, char **envp) { + /* get the prefix and the suffix of the sdcc command */ + getPrefixSuffix(argv[0]); + + /* set a larger stack size of a running sdcc process to 4MB */ + setStackSize(); + /* turn all optimizations off by default */ memset(&optimize, 0, sizeof(struct optimize)); diff --git a/src/SDCCmem.c b/src/SDCCmem.c index 430fc2143..e657b3a08 100644 --- a/src/SDCCmem.c +++ b/src/SDCCmem.c @@ -85,7 +85,7 @@ memmap *allocMap(char rspace, /* sfr space */ exit(1); } - memset(map, ZERO, sizeof(memmap)); + memset(map, 0, sizeof(memmap)); map->regsp = rspace; map->fmap = farmap; map->paged = paged; @@ -353,7 +353,7 @@ void initMem() { eeprom = allocMap(0, 1, 0, 0, 0, 0, 0, REG_NAME, 'K', EEPPOINTER); /* the unknown map */ - generic = allocMap(1, 0, 0, 1, 1, 0, 0, REG_NAME, ' ', GPOINTER); + generic = allocMap(0, 0, 0, 0, 0, 0, 0, DATA_NAME, ' ', GPOINTER); } /*-----------------------------------------------------------------*/ @@ -362,23 +362,23 @@ void initMem() { void allocIntoSeg(symbol *sym) { memmap *segment; - if (SPEC_ADDRSPACE(sym->etype)) { + const symbol *symbolspace = getAddrspace(sym->type); + + if (symbolspace) { namedspacemap *nm; for (nm = namedspacemaps; nm; nm = nm->next) - if (!strcmp(nm->name, SPEC_ADDRSPACE(sym->etype)->name)) + if (!strcmp(nm->name, symbolspace->name)) break; if (!nm) { nm = Safe_alloc(sizeof(namedspacemap)); - nm->name = Safe_alloc(strlen(SPEC_ADDRSPACE(sym->etype)->name) + 1); - strcpy(nm->name, SPEC_ADDRSPACE(sym->etype)->name); - nm->is_const = (SPEC_ADDRSPACE(sym->etype)->type && - SPEC_CONST(SPEC_ADDRSPACE(sym->etype)->type)); - nm->map = nm->is_const - ? allocMap(0, 1, 0, 0, 0, 1, options.code_loc, - SPEC_ADDRSPACE(sym->etype)->name, 'C', CPOINTER) - : allocMap(0, 0, 0, 1, 0, 0, options.data_loc, - SPEC_ADDRSPACE(sym->etype)->name, 'E', POINTER); + nm->name = Safe_alloc(strlen(symbolspace->name) + 1); + strcpy(nm->name, symbolspace->name); + nm->is_const = (symbolspace->type && SPEC_CONST(symbolspace->type)); + nm->map = nm->is_const ? allocMap(0, 1, 0, 0, 0, 1, options.code_loc, + symbolspace->name, 'C', CPOINTER) + : allocMap(0, 0, 0, 1, 0, 0, options.data_loc, + symbolspace->name, 'E', POINTER); nm->next = namedspacemaps; namedspacemaps = nm; } @@ -387,7 +387,12 @@ void allocIntoSeg(symbol *sym) { return; } - segment = SPEC_OCLS(sym->etype); + if (!(segment = SPEC_OCLS(sym->etype))) { + fprintf(stderr, "Symbol %s:\n", sym->name); + wassertl(0, "Failed to allocate symbol to memory segment due to missing " + "output storage class"); + return; + } addSet(&segment->syms, sym); if (segment == pdata) sym->iaccess = 1; @@ -520,7 +525,8 @@ void allocGlobal(symbol *sym) { interrupts[FUNC_INTNO(sym->type)] = sym; /* automagically extend the maximum interrupts */ - if (FUNC_INTNO(sym->type) >= maxInterrupts) + if (FUNC_INTNO(sym->type) >= maxInterrupts && + FUNC_INTNO(sym->type) != INTNO_TRAP) maxInterrupts = FUNC_INTNO(sym->type) + 1; } /* if it is not compiler defined */ @@ -537,10 +543,9 @@ void allocGlobal(symbol *sym) { return; } - if (sym->level) - /* register storage class ignored changed to FIXED */ - if (SPEC_SCLS(sym->etype) == S_REGISTER) - SPEC_SCLS(sym->etype) = S_FIXED; + /* register storage class ignored changed to FIXED */ + if (SPEC_SCLS(sym->etype) == S_REGISTER) + SPEC_SCLS(sym->etype) = S_FIXED; /* if it is fixed, then allocate depending on the */ /* current memory model, same for automatics */ @@ -570,11 +575,28 @@ void allocGlobal(symbol *sym) { /*-----------------------------------------------------------------*/ /* allocParms - parameters are always passed on stack */ /*-----------------------------------------------------------------*/ -void allocParms(value *val) { +void allocParms(value *val, bool smallc) { value *lval; int pNum = 1; + int stackParamSizeAdjust = 0; + + if (smallc) { + for (lval = val; lval; lval = lval->next) { + if (IS_REGPARM(lval->etype)) + continue; + stackParamSizeAdjust += getSize(lval->type) + (getSize(lval->type) == 1); + } + } + stackPtr += stackParamSizeAdjust; for (lval = val; lval; lval = lval->next, pNum++) { + if (!lval->sym) // Can only happen if there was a syntax error in the + // declaration. + { + fatalError++; + return; + } + /* check the declaration */ checkDecl(lval->sym, 0); @@ -590,27 +612,35 @@ void allocParms(value *val) { /* if automatic variables r 2b stacked */ if (options.stackAuto || IFFUNC_ISREENT(currFunc->type)) { + int paramsize = + getSize(lval->type) + (getSize(lval->type) == 1 && (smallc)); + if (lval->sym) lval->sym->onStack = 1; /* choose which stack 2 use */ - /* use xternal stack */ - if (options.useXstack) { + if (options.useXstack) /* use external stack */ + { /* PENDING: stack direction support */ + wassertl( + !smallc, + "SmallC calling convention not yet supported for xstack callee"); SPEC_OCLS(lval->etype) = SPEC_OCLS(lval->sym->etype) = xstack; SPEC_STAK(lval->etype) = SPEC_STAK(lval->sym->etype) = - lval->sym->stack = xstackPtr - getSize(lval->type); - xstackPtr -= getSize(lval->type); - } else { /* use internal stack */ + lval->sym->stack = xstackPtr - paramsize; + xstackPtr -= paramsize; + } else /* use internal stack */ + { + SPEC_OCLS(lval->etype) = SPEC_OCLS(lval->sym->etype) = istack; - if (port->stack.direction > 0) { + if ((port->stack.direction > 0) != smallc) { SPEC_STAK(lval->etype) = SPEC_STAK( lval->sym->etype) = lval->sym->stack = stackPtr - (FUNC_REGBANK(currFunc->type) ? port->stack.bank_overhead : 0) - - getSize(lval->type) - + paramsize - (FUNC_ISISR(currFunc->type) ? port->stack.isr_overhead : 0); - stackPtr -= getSize(lval->type); + stackPtr -= paramsize; } else { /* This looks like the wrong order but it turns out OK... */ /* PENDING: isr, bank overhead, ... */ @@ -619,11 +649,18 @@ void allocParms(value *val) { stackPtr + (FUNC_ISISR(currFunc->type) ? port->stack.isr_overhead : 0) + 0; - stackPtr += getSize(lval->type); + stackPtr += paramsize; } } allocIntoSeg(lval->sym); - } else { /* allocate them in the automatic space */ + } else { + /* Do not allocate for inline functions to avoid multiple definitions - + * see bug report #2591. */ + if (IFFUNC_ISINLINE(currFunc->type) && !IS_STATIC(currFunc->etype) && + !IS_EXTERN(currFunc->etype)) + continue; + + /* allocate them in the automatic space */ /* generate a unique name */ SNPRINTF(lval->sym->rname, sizeof(lval->sym->rname), "%s%s_PARM_%d", port->fun_prefix, currFunc->name, pNum); @@ -638,10 +675,24 @@ void allocParms(value *val) { /* otherwise depending on the memory model */ SPEC_OCLS(lval->etype) = SPEC_OCLS(lval->sym->etype) = port->mem.default_local_map; - SPEC_SCLS(lval->etype) = S_XDATA; + if (options.model == MODEL_SMALL) { + /* note here that we put it into the overlay segment + first, we will remove it from the overlay segment + after the overlay determination has been done */ + if (!options.noOverlay) { + SPEC_OCLS(lval->etype) = SPEC_OCLS(lval->sym->etype) = overlay; + } + } else if (options.model == MODEL_MEDIUM) { + SPEC_SCLS(lval->etype) = S_PDATA; + } else { + SPEC_SCLS(lval->etype) = S_XDATA; + } allocIntoSeg(lval->sym); } } + + stackPtr -= stackParamSizeAdjust; + return; } @@ -652,6 +703,9 @@ void deallocParms(value *val) { value *lval; for (lval = val; lval; lval = lval->next) { + if (!lval->sym) /* Syntax error in declaration */ + continue; + /* unmark is myparm */ lval->sym->ismyparm = 0; @@ -711,13 +765,13 @@ void allocLocal(symbol *sym) { } /* if volatile then */ - if (IS_VOLATILE(sym->etype)) + if (IS_VOLATILE(sym->type)) sym->allocreq = 1; /* this is automatic */ /* if it's to be placed on the stack */ - if (options.stackAuto || reentrant) { + if (options.stackAuto) { sym->onStack = 1; if (options.useXstack) { /* PENDING: stack direction for xstack */ @@ -741,7 +795,7 @@ void allocLocal(symbol *sym) { /* else depending on the storage class specified */ /* if this is a function then assign code space */ - if (IS_FUNC(sym->type)) { + if (IS_FUNC(sym->type) && !sym->isitmp) { SPEC_OCLS(sym->etype) = code; return; } @@ -768,7 +822,12 @@ void allocLocal(symbol *sym) { /* again note that we have put it into the overlay segment will remove and put into the 'data' segment if required after overlay analysis has been done */ - SPEC_OCLS(sym->etype) = port->mem.default_local_map; + if (options.model == MODEL_SMALL) { + SPEC_OCLS(sym->etype) = + (options.noOverlay ? port->mem.default_local_map : overlay); + } else { + SPEC_OCLS(sym->etype) = port->mem.default_local_map; + } allocIntoSeg(sym); } @@ -839,7 +898,7 @@ int allocVariables(symbol *symChain) { symbol *sym; symbol *csym; int stack = 0; - int saveLevel = 0; + long saveLevel = 0; /* go thru the symbol chain */ for (sym = symChain; sym; sym = sym->next) { @@ -848,7 +907,11 @@ int allocVariables(symbol *symChain) { if (IS_TYPEDEF(sym->etype)) { /* check if the typedef already exists */ csym = findSym(TypedefTab, NULL, sym->name); - if (csym && csym->level == sym->level) + if (csym && csym->level == sym->level && + !(options.std_c11 && + compareTypeExact( + sym->type, csym->type, + -1))) /* typedef to same type not allowed before ISO C11 */ werror(E_DUPLICATE_TYPEDEF, sym->name); SPEC_EXTR(sym->etype) = 0; @@ -897,6 +960,27 @@ int allocVariables(symbol *symChain) { return stack; } +void clearStackOffsets(void) { + const symbol *sym; + + for (sym = setFirstItem(istack->syms); sym; sym = setNextItem(istack->syms)) { + const int size = getSize(sym->type); + + /* nothing to do with parameters so continue */ + if ((sym->_isparm && !IS_REGPARM(sym->etype))) + continue; + + currFunc->stack -= size; + SPEC_STAK(currFunc->etype) -= size; + } + + if (currFunc) { + // wassert(!(currFunc->stack)); // Sometimes some local variable was + // included in istack->sams. + currFunc->stack = 0; + } +} + #define BTREE_STACK 1 /*-----------------------------------------------------------------*/ @@ -1034,6 +1118,12 @@ static int printAllocInfoSeg(memmap *map, symbol *func, struct dbuf_s *oBuf) { stack_offset = func->stack; } + stack_offset += port->stack.offset; /* in case sp/bp points to the next + location instead of last */ + + if (port->stack.direction < 0) + stack_offset = -stack_offset; + dbuf_printf(oBuf, "to stack - %s %+d\n", SYM_BP(sym), sym->stack - stack_offset); continue; diff --git a/src/SDCCmem.h b/src/SDCCmem.h index 7da4da2e6..261a5e88e 100644 --- a/src/SDCCmem.h +++ b/src/SDCCmem.h @@ -113,13 +113,14 @@ void initMem(); bool defaultOClass(struct symbol *); void allocGlobal(struct symbol *); void allocLocal(struct symbol *); -void allocParms(struct value *); +void allocParms(struct value *, bool smallc); void deallocParms(struct value *); void deallocLocal(struct symbol *); int allocVariables(struct symbol *); void overlay2Set(); void overlay2data(); -void redoStackOffsets(); +void clearStackOffsets(void); +void redoStackOffsets(void); void printAllocInfo(struct symbol *, struct dbuf_s *); void doOverlays(struct eBBlock **, int count); void deleteFromSeg(struct symbol *); diff --git a/src/SDCCnaddr.cc b/src/SDCCnaddr.cc index 25f901824..c09b16468 100644 --- a/src/SDCCnaddr.cc +++ b/src/SDCCnaddr.cc @@ -71,7 +71,7 @@ void create_cfg_naddr(cfg_t &cfg, iCode *start_ic, ebbIndex *ebbi) { int switchAddressSpacesOptimally(iCode *ic, ebbIndex *ebbi) { cfg_t control_flow_graph; - tree_dec_naddr_t tree_decomposition; + tree_dec_t tree_decomposition; std::map addrspaces; create_cfg_naddr(control_flow_graph, ic, ebbi); @@ -80,8 +80,10 @@ int switchAddressSpacesOptimally(iCode *ic, ebbIndex *ebbi) { if (options.dump_graphs) dump_cfg_naddr(control_flow_graph); - thorup_tree_decomposition(tree_decomposition, control_flow_graph); - nicify(tree_decomposition); + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + if (options.dump_graphs) + dump_tree_decomposition_naddr(tree_decomposition); return (tree_dec_address_switch(tree_decomposition, control_flow_graph, addrspaces)); diff --git a/src/SDCCnaddr.hpp b/src/SDCCnaddr.hpp index 10bd9a484..2a3a60d2b 100644 --- a/src/SDCCnaddr.hpp +++ b/src/SDCCnaddr.hpp @@ -35,31 +35,26 @@ #include #include -#include +// Workaround for boost bug #11880 +#include +#if BOOST_VERSION == 106000 +#include +#endif -#include "SDCCtree_dec.hpp" +#include extern "C" { -#include "SDCCBBlock.h" +#include "SDCCsymt.h" #include "SDCCicode.h" +#include "SDCCBBlock.h" #include "SDCCopt.h" -#include "SDCCsymt.h" #include "SDCCy.h" } -#ifdef HAVE_STX_BTREE_SET_H -#include -#endif - typedef short int naddrspace_t; // Named address spaces. -1: Undefined, Others: see map. -#ifdef HAVE_STX_BTREE_SET_H -typedef stx::btree_set - naddrspaceset_t; // Faster than std::set -#else typedef std::set naddrspaceset_t; -#endif struct assignment_naddr { float s; @@ -126,7 +121,14 @@ typedef boost::adjacency_list - tree_dec_naddr_t; + tree_dec_t; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif + +#include "SDCCtree_dec.hpp" // Annotate nodes of the control flow graph with the set of possible named // address spaces active there. @@ -486,7 +488,35 @@ void dump_cfg_naddr(const cfg_t &cfg) { os << *n << " "; name[i] = os.str(); } - boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name)); + boost::write_graphviz( + dump_file, cfg, boost::make_label_writer(name), boost::default_writer(), + cfg_titlewriter(currFunc->rname, " bank selection instr. placement")); + delete[] name; +} + +// Dump tree decomposition, show bag and live variables at each node. +static void dump_tree_decomposition_naddr(const tree_dec_t &tree_dec) { + std::ofstream dump_file( + (std::string(dstFileName) + ".dumpnaddrdec" + currFunc->rname + ".dot") + .c_str()); + + unsigned int w = 0; + + std::string *name = new std::string[num_vertices(tree_dec)]; + for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) { + if (tree_dec[i].bag.size() > w) + w = tree_dec[i].bag.size(); + std::ostringstream os; + std::set::const_iterator v1; + os << i << " | "; + for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) + os << *v1 << " "; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name), + boost::default_writer(), + dec_titlewriter((w - 1), currFunc->rname, + " bank selection instr. placement")); delete[] name; } diff --git a/src/SDCCopt.c b/src/SDCCopt.c index 7a732887f..cfde5466c 100644 --- a/src/SDCCopt.c +++ b/src/SDCCopt.c @@ -27,6 +27,7 @@ -------------------------------------------------------------------------*/ #include "common.h" +#include "dbuf_string.h" #include /*-----------------------------------------------------------------*/ @@ -94,16 +95,14 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { case '<': func = fslt; break; - case '>': - func = fsgt; - break; - case LE_OP: - func = fslteq; - break; - case GE_OP: - func = fsgteq; + case '>': { + operand *tmp = right; + right = left; + left = tmp; + func = fslt; break; } + } } else if (IS_FIXED16X16(operandType(right))) { switch (ic->op) { case '+': @@ -150,6 +149,7 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -164,6 +164,7 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, right); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)->next); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -177,9 +178,12 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { } else { newic = newiCode(IPUSH, right, NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(right)); + bytesPushed += getSize(operandType(right)) + + (getSize(operandType(right)) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -193,8 +197,11 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { } else { newic = newiCode(IPUSH, left, NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(left)); + bytesPushed += getSize(operandType(left)) + + (getSize(operandType(left)) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -214,6 +221,7 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); } @@ -223,7 +231,8 @@ static void cnvToFcall(iCode *ic, eBBlock *ebp) { static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { iCode *ip, *newic; symbol *func = NULL; - sym_link *type = operandType(IC_RIGHT(ic)); + sym_link *type = copyLinkChain(operandType(IC_RIGHT(ic))); + SPEC_SHORT(type) = 0; int linenno = ic->lineno; int bwd, su; int bytesPushed = 0; @@ -251,6 +260,12 @@ static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { goto found; } + if (IS_BOOLEAN(type)) { + wassert(multypes[0][1] == UCHARTYPE); + func = conv[0][0][1]; + goto found; + } + assert(0); found: @@ -264,6 +279,7 @@ static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_RIGHT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -277,8 +293,11 @@ static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { } else { newic = newiCode(IPUSH, IC_RIGHT(ic), NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(IC_RIGHT(ic))); + bytesPushed += getSize(operandType(IC_RIGHT(ic))) + + (getSize(operandType(IC_RIGHT(ic))) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -294,6 +313,7 @@ static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -307,7 +327,8 @@ static void cnvToFloatCast(iCode *ic, eBBlock *ebp) { static void cnvToFixed16x16Cast(iCode *ic, eBBlock *ebp) { iCode *ip, *newic; symbol *func = NULL; - sym_link *type = operandType(IC_RIGHT(ic)); + sym_link *type = copyLinkChain(operandType(IC_RIGHT(ic))); + SPEC_SHORT(type) = 0; int linenno = ic->lineno; int bwd, su; int bytesPushed = 0; @@ -342,6 +363,7 @@ static void cnvToFixed16x16Cast(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_RIGHT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -357,6 +379,7 @@ static void cnvToFixed16x16Cast(iCode *ic, eBBlock *ebp) { newic->parmPush = 1; bytesPushed += getSize(operandType(IC_RIGHT(ic))); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -372,6 +395,7 @@ static void cnvToFixed16x16Cast(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = linenno; @@ -385,7 +409,8 @@ static void cnvToFixed16x16Cast(iCode *ic, eBBlock *ebp) { static void cnvFromFloatCast(iCode *ic, eBBlock *ebp) { iCode *ip, *newic; symbol *func = NULL; - sym_link *type = operandType(IC_LEFT(ic)); + sym_link *type = copyLinkChain(operandType(IC_LEFT(ic))); + SPEC_SHORT(type) = 0; char *filename = ic->filename; int lineno = ic->lineno; int bwd, su; @@ -421,6 +446,7 @@ static void cnvFromFloatCast(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_RIGHT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -434,8 +460,11 @@ static void cnvFromFloatCast(iCode *ic, eBBlock *ebp) { } else { newic = newiCode(IPUSH, IC_RIGHT(ic), NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(IC_RIGHT(ic))); + bytesPushed += getSize(operandType(IC_RIGHT(ic))) + + (getSize(operandType(IC_RIGHT(ic))) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -451,6 +480,7 @@ static void cnvFromFloatCast(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -464,7 +494,8 @@ static void cnvFromFloatCast(iCode *ic, eBBlock *ebp) { static void cnvFromFixed16x16Cast(iCode *ic, eBBlock *ebp) { iCode *ip, *newic; symbol *func = NULL; - sym_link *type = operandType(IC_LEFT(ic)); + sym_link *type = copyLinkChain(operandType(IC_LEFT(ic))); + SPEC_SHORT(type) = 0; char *filename = ic->filename; int lineno = ic->lineno; int bwd, su; @@ -506,6 +537,7 @@ static void cnvFromFixed16x16Cast(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_RIGHT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -521,6 +553,7 @@ static void cnvFromFixed16x16Cast(iCode *ic, eBBlock *ebp) { newic->parmPush = 1; bytesPushed += getSize(operandType(IC_RIGHT(ic))); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -536,6 +569,7 @@ static void cnvFromFixed16x16Cast(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -560,8 +594,10 @@ static void convilong(iCode *ic, eBBlock *ebp) { int bytesPushed = 0; operand *left; operand *right; - sym_link *leftType = operandType(IC_LEFT(ic)); - sym_link *rightType = operandType(IC_RIGHT(ic)); + sym_link *leftType = copyLinkChain(operandType(IC_LEFT(ic))); + sym_link *rightType = copyLinkChain(operandType(IC_RIGHT(ic))); + SPEC_SHORT(leftType) = 0; + SPEC_SHORT(rightType) = 0; remiCodeFromeBBlock(ebp, ic); @@ -573,6 +609,52 @@ static void convilong(iCode *ic, eBBlock *ebp) { if (IS_SYMOP(right)) bitVectUnSetBit(OP_USES(right), ic->key); + if (op == '*' && (muls16tos32[0] || muls16tos32[1]) && + (IS_SYMOP(left) && bitVectnBitsOn(OP_DEFS(left)) == 1 && + bitVectnBitsOn(OP_USES(left)) == 0 || + IS_OP_LITERAL(left) && operandLitValue(left) < 32768 && + operandLitValue(left) >= -32768) && + (IS_SYMOP(right) && bitVectnBitsOn(OP_DEFS(right)) == 1 && + bitVectnBitsOn(OP_USES(right)) == 0 || + IS_OP_LITERAL(right) && operandLitValue(right) < 32768 && + operandLitValue(right) >= -32768) && + getSize(leftType) == 4 && getSize(rightType) == 4) { + iCode *lic = + IS_SYMOP(left) + ? hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(left))) + : 0; + iCode *ric = + IS_SYMOP(right) + ? hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(right))) + : 0; + + if ((!lic || lic->op == CAST && getSize(operandType(IC_RIGHT(lic))) == 2 && + SPEC_USIGN(operandType(IC_RIGHT(lic))) == + SPEC_USIGN(operandType(left))) && + (!ric || ric->op == CAST && getSize(operandType(IC_RIGHT(ric))) == 2 && + SPEC_USIGN(operandType(IC_RIGHT(ric))) == + SPEC_USIGN(operandType(right)))) { + func = muls16tos32[SPEC_USIGN(operandType(left))]; + + if (lic) { + lic->op = '='; + OP_SYMBOL(left)->type = newIntLink(); + } else + IC_LEFT(ic) = operandFromValue(valCastLiteral( + newIntLink(), operandLitValue(left), operandLitValue(left))); + + if (ric) { + ric->op = '='; + OP_SYMBOL(right)->type = newIntLink(); + } else + IC_RIGHT(ic) = operandFromValue(valCastLiteral( + newIntLink(), operandLitValue(right), operandLitValue(right))); + + if (func) + goto found; + } + } + if (getSize(leftType) == 1 && getSize(rightType) == 1) { int muldivmod; @@ -637,6 +719,7 @@ static void convilong(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_LEFT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -651,6 +734,7 @@ static void convilong(iCode *ic, eBBlock *ebp) { newic = newiCode('=', NULL, IC_RIGHT(ic)); IC_RESULT(newic) = operandFromValue(FUNC_ARGS(func->type)->next); } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -666,8 +750,11 @@ static void convilong(iCode *ic, eBBlock *ebp) { newic = newiCode(IPUSH, IC_RIGHT(ic), NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(IC_RIGHT(ic))); + bytesPushed += getSize(operandType(IC_RIGHT(ic))) + + (getSize(operandType(IC_RIGHT(ic))) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -682,8 +769,11 @@ static void convilong(iCode *ic, eBBlock *ebp) { newic = newiCode(IPUSH, IC_LEFT(ic), NULL); newic->parmPush = 1; - bytesPushed += getSize(operandType(IC_LEFT(ic))); + bytesPushed += getSize(operandType(IC_LEFT(ic))) + + (getSize(operandType(IC_LEFT(ic))) % 2 && + false); // pdk requires stack to be even-aligned } + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); newic->filename = filename; newic->lineno = lineno; @@ -704,6 +794,7 @@ static void convilong(iCode *ic, eBBlock *ebp) { if (currFunc) FUNC_HASFCALL(currFunc->type) = 1; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebp, newic, ip); } @@ -747,7 +838,7 @@ static void convbuiltin(iCode *const ic, eBBlock *ebp) { strcpy(OP_SYMBOL(IC_LEFT(icc))->rname, !strcmp(bif->name, "__builtin_memcpy") - ? "_memcpy" + ? "___memcpy" : (!strcmp(bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); goto convert; @@ -757,14 +848,15 @@ static void convbuiltin(iCode *const ic, eBBlock *ebp) { !strcmp(bif->name, "__builtin_strncpy") || !strcmp(bif->name, "__builtin_memset")) { /* Replace iff return value is used or last parameter is not an integer - * constant. */ + * constant (except for memcpy, where non-integers can be handled). */ if (bitVectIsZero(OP_USES(IC_RESULT(icc))) && - IS_OP_LITERAL(IC_LEFT(lastparam))) + (IS_OP_LITERAL(IC_LEFT(lastparam)) || + !strcmp(bif->name, "__builtin_memcpy"))) return; strcpy(OP_SYMBOL(IC_LEFT(icc))->rname, !strcmp(bif->name, "__builtin_memcpy") - ? "_memcpy" + ? "___memcpy" : (!strcmp(bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); goto convert; @@ -807,9 +899,8 @@ static void convsmallc(iCode *ic, eBBlock *ebp) { assert(ic->op == CALL || ic->op == PCALL); for (icc = ic->prev; icc && icc->op == IPUSH; icc = icc->prev) - ; + ic = icc; icp = icc; - ic = icp->next; /* Reverse parameters. */ for (icc = ic; icc->op != CALL && icc->op != PCALL; icc = icc->next) { @@ -821,6 +912,8 @@ static void convsmallc(iCode *ic, eBBlock *ebp) { if (icc != ic) { if (icp) icp->next = icc->prev; + else + ebp->sch = icc->prev; icc->prev = ic; } for (; icc != icp; ico = icc, icc = icc->prev) { @@ -863,31 +956,73 @@ static void convertToFcall(eBBlock **ebbs, int count) { // Easy special case which avoids function call: modulo by a literal power // of two can be replaced by a bitwise AND. - if (ic->op == '%' && isOperandLiteral(IC_RIGHT(ic)) && - IS_UNSIGNED(operandType(IC_LEFT(ic)))) { - unsigned litVal = - abs((unsigned)double2ul(operandLitValue(IC_RIGHT(ic)))); - - /* modulo by 1: no remainder */ - if (litVal == 1) { - ic->op = '='; - IC_RIGHT(ic) = operandFromLit(0); - IC_LEFT(ic) = NULL; - continue; - } - // See if literal value is a power of 2. - while (litVal && !(litVal & 1)) { - litVal >>= 1; - } - if (litVal) { - // discard lowest set bit. - litVal >>= 1; + if (ic->op == '%' && isOperandLiteral(IC_RIGHT(ic))) { + bool us = IS_UNSIGNED(operandType(IC_LEFT(ic))); + bool upcast = FALSE; + iCode *dic = NULL; + + // Chek if left really is just an upcasted unsigned value. + if (!us && IS_SYMOP(IC_LEFT(ic)) && + bitVectnBitsOn(OP_DEFS(IC_LEFT(ic))) == 1) { + dic = + hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(IC_LEFT(ic)))); + + if (dic && dic->op == CAST && + IS_UNSIGNED(operandType(IC_RIGHT(dic))) && + getSize(operandType(IC_RIGHT(dic))) < + getSize(operandType(IC_RESULT(dic)))) + us = upcast = true; } - if (!litVal) { - ic->op = BITWISEAND; - IC_RIGHT(ic) = operandFromLit(operandLitValue(IC_RIGHT(ic)) - 1); - continue; + if (us) { + unsigned long litVal = double2ul(operandLitValue(IC_RIGHT(ic))); + + /* modulo by 1: no remainder */ + if (litVal == 1) { + ic->op = '='; + IC_RIGHT(ic) = operandFromLit(0); + if (IS_SYMOP(IC_LEFT(ic))) + bitVectUnSetBit(OP_USES(IC_LEFT(ic)), ic->key); + IC_LEFT(ic) = NULL; + continue; + } + // See if literal value is a power of 2. + while (litVal && !(litVal & 1)) { + litVal >>= 1; + } + if (litVal) { + // discard lowest set bit. + litVal >>= 1; + } + + if (!litVal) { + ic->op = BITWISEAND; + IC_RIGHT(ic) = operandFromLit(operandLitValue(IC_RIGHT(ic)) - 1); + if (upcast && IS_CHAR(operandType(IC_RIGHT(dic))) && + bitVectnBitsOn(OP_USES(IC_LEFT(ic))) == 1) { + // Use precasted value + attachiCodeOperand(IC_RIGHT(dic), &IC_LEFT(ic), ic); + // Change cast to assignmnent to self to avoid + // reading IC_RIGHT (dic) twice in case it + // was volatile + attachiCodeOperand(IC_RESULT(dic), &IC_RIGHT(dic), dic); + dic->op = '='; + // If upcast from char, maybe there's a + // corresponding downcast to char that could + // be eliminated too + if (bitVectnBitsOn(OP_USES(IC_RESULT(ic))) == 1) { + iCode *uic; + uic = hTabItemWithKey(iCodehTab, + bitVectFirstBit(OP_USES(IC_RESULT(ic)))); + if (uic->op == CAST && IS_CHAR(operandType(IC_RESULT(uic)))) { + attachiCodeOperand(IC_RESULT(uic), &IC_RESULT(ic), ic); + attachiCodeOperand(IC_RESULT(uic), &IC_RIGHT(uic), uic); + uic->op = '='; + } + } + } + continue; + } } } @@ -895,7 +1030,7 @@ static void convertToFcall(eBBlock **ebbs, int count) { if (ic->op == '*' || ic->op == '/' || ic->op == '%') { sym_link *leftType = operandType(IC_LEFT(ic)); - if (IS_INTEGRAL(leftType) && getSize(leftType) > port->support.muldiv) { + if (IS_INTEGRAL(leftType)) { sym_link *rightType = operandType(IC_RIGHT(ic)); if (port->hasNativeMulFor != NULL && @@ -911,7 +1046,8 @@ static void convertToFcall(eBBlock **ebbs, int count) { ic->op == RIGHT_OP) { sym_link *type = operandType(IC_LEFT(ic)); - if (IS_INTEGRAL(type) && getSize(type) > port->support.shift && + if (IS_INTEGRAL(type) && + getSize(type) > (unsigned)port->support.shift && port->support.shift >= 0) { convilong(ic, ebbs[i]); } @@ -1069,7 +1205,7 @@ static void separateAddressSpaces(eBBlock **ebbs, int count) { /*printf ("Looking at ic %d, op %d\n", ic->key, (int)(ic->op));*/ - if (left && IS_SYMOP(left)) { + if (left && ic->op != ADDRESS_OF && IS_SYMOP(left)) { if (POINTER_GET(ic)) { assert(!(IS_DECL(OP_SYMBOL(left)->type) && DCL_PTR_ADDRSPACE(OP_SYMBOL(left)->type))); @@ -1135,6 +1271,7 @@ static void separateAddressSpaces(eBBlock **ebbs, int count) { if (newic) { newic->filename = ic->filename; newic->lineno = ic->lineno; + hTabAddItem(&iCodehTab, newic->key, newic); addiCodeToeBBlock(ebbs[i], newic, iic); } @@ -1171,7 +1308,7 @@ const symbol *getAddrspaceiCode(const iCode *ic) { /* Previous transformations in separateAddressSpaces() should ensure that at most one addressspace occours in each iCode. */ - if (left && IS_SYMOP(left)) { + if (left && ic->op != ADDRESS_OF && IS_SYMOP(left)) { if (POINTER_GET(ic)) { assert(!(IS_DECL(OP_SYMBOL(left)->type) && DCL_PTR_ADDRSPACE(OP_SYMBOL(left)->type))); @@ -1273,6 +1410,34 @@ static int isLocalWithoutDef(symbol *sym) { return !sym->defs; } +static void replaceRegEqvOperand(iCode *ic, operand **opp, int force_isaddr, + int new_isaddr) { + operand *op = *opp; + symbol *sym = OP_SYMBOL(op); + + if (isLocalWithoutDef(sym)) { + werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->name); + OP_REQV(op) = NULL; + sym->allocreq = 1; + } else if (OP_REQV(op)) { + operand *nop; + + nop = operandFromOperand(OP_REQV(op)); + + /* Copy def/use info from true symbol to register equivalent */ + /* but only if this hasn't been done already. */ + if (!OP_DEFS(nop)) + OP_DEFS(nop) = bitVectCopy(OP_DEFS(op)); + if (!OP_USES(nop)) + OP_USES(nop) = bitVectCopy(OP_USES(op)); + + if (force_isaddr) + nop->isaddr = new_isaddr; + + *opp = nop; /* Replace true sym operand with reg equiv */ + } +} + /*-----------------------------------------------------------------*/ /* replaceRegEqv - replace all local variables with their reqv */ /*-----------------------------------------------------------------*/ @@ -1281,12 +1446,8 @@ static void replaceRegEqv(ebbIndex *ebbi) { int count = ebbi->count; int i; - /* Update the symbols' def bitvector so we know if there is */ - /* a defining iCode or not. Only replace a local variable */ - /* with its register equivalent if there is a defining iCode; */ - /* otherwise, the port's register allocater may choke. */ - cseAllBlocks(ebbi, TRUE); - + /* Reset all the def/use info (Otherwise there may be stale def/use */ + /* info if a variable is also used in a previous functions) */ for (i = 0; i < count; i++) { iCode *ic; @@ -1298,85 +1459,82 @@ static void replaceRegEqv(ebbIndex *ebbi) { continue; if (ic->op == IFX) { - if (IC_COND(ic) && IS_TRUE_SYMOP(IC_COND(ic)) && - isLocalWithoutDef(OP_SYMBOL(IC_COND(ic)))) { - werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, - OP_SYMBOL(IC_COND(ic))->name); - OP_REQV(IC_COND(ic)) = NULL; - OP_SYMBOL(IC_COND(ic))->allocreq = 1; + if (IS_TRUE_SYMOP(IC_COND(ic))) { + OP_DEFS(IC_COND(ic)) = NULL; + OP_USES(IC_COND(ic)) = NULL; } - - if (IS_TRUE_SYMOP(IC_COND(ic)) && OP_REQV(IC_COND(ic))) - IC_COND(ic) = opFromOpWithDU( - OP_REQV(IC_COND(ic)), OP_DEFS(IC_COND(ic)), OP_USES(IC_COND(ic))); continue; } if (ic->op == JUMPTABLE) { - if (IC_JTCOND(ic) && IS_TRUE_SYMOP(IC_JTCOND(ic)) && - isLocalWithoutDef(OP_SYMBOL(IC_JTCOND(ic)))) { - werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, - OP_SYMBOL(IC_JTCOND(ic))->name); - OP_REQV(IC_JTCOND(ic)) = NULL; - OP_SYMBOL(IC_JTCOND(ic))->allocreq = 1; + if (IS_TRUE_SYMOP(IC_JTCOND(ic))) { + OP_DEFS(IC_JTCOND(ic)) = NULL; + OP_USES(IC_JTCOND(ic)) = NULL; } - - if (IS_TRUE_SYMOP(IC_JTCOND(ic)) && OP_REQV(IC_JTCOND(ic))) - IC_JTCOND(ic) = - opFromOpWithDU(OP_REQV(IC_JTCOND(ic)), OP_DEFS(IC_JTCOND(ic)), - OP_USES(IC_JTCOND(ic))); continue; } - if (ic->op == RECEIVE) { - if (OP_SYMBOL(IC_RESULT(ic))->addrtaken) - OP_SYMBOL(IC_RESULT(ic))->isspilt = 1; + if (IS_TRUE_SYMOP(IC_RESULT(ic))) { + OP_DEFS(IC_RESULT(ic)) = NULL; + OP_USES(IC_RESULT(ic)) = NULL; } - /* general case */ - if (IC_RESULT(ic) && IS_TRUE_SYMOP(IC_RESULT(ic)) && - OP_REQV(IC_RESULT(ic))) { - if (POINTER_SET(ic)) { - IC_RESULT(ic) = - opFromOpWithDU(OP_REQV(IC_RESULT(ic)), OP_DEFS(IC_RESULT(ic)), - OP_USES(IC_RESULT(ic))); - IC_RESULT(ic)->isaddr = 1; - } else { - IC_RESULT(ic) = - opFromOpWithDU(OP_REQV(IC_RESULT(ic)), OP_DEFS(IC_RESULT(ic)), - OP_USES(IC_RESULT(ic))); - } + if (IS_TRUE_SYMOP(IC_RIGHT(ic))) { + OP_DEFS(IC_RIGHT(ic)) = NULL; + OP_USES(IC_RIGHT(ic)) = NULL; + } + + if (IS_TRUE_SYMOP(IC_LEFT(ic))) { + OP_DEFS(IC_LEFT(ic)) = NULL; + OP_USES(IC_LEFT(ic)) = NULL; } + } + } + + /* Update the symbols' def bitvector so we know if there is */ + /* a defining iCode or not. Only replace a local variable */ + /* with its register equivalent if there is a defining iCode; */ + /* otherwise, the port's register allocater may choke. */ + cseAllBlocks(ebbi, TRUE); + + for (i = 0; i < count; i++) { + iCode *ic; - if (IC_RIGHT(ic) && IS_TRUE_SYMOP(IC_RIGHT(ic)) && - isLocalWithoutDef(OP_SYMBOL(IC_RIGHT(ic)))) { - werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, - OP_SYMBOL(IC_RIGHT(ic))->name); - OP_REQV(IC_RIGHT(ic)) = NULL; - OP_SYMBOL(IC_RIGHT(ic))->allocreq = 1; + if (ebbs[i]->noPath) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + if (SKIP_IC2(ic)) + continue; + + if (ic->op == IFX) { + if (IS_TRUE_SYMOP(IC_COND(ic))) + replaceRegEqvOperand(ic, &IC_COND(ic), 0, 0); + continue; } - if (IC_RIGHT(ic) && IS_TRUE_SYMOP(IC_RIGHT(ic)) && - OP_REQV(IC_RIGHT(ic))) { - IC_RIGHT(ic) = - opFromOpWithDU(OP_REQV(IC_RIGHT(ic)), OP_DEFS(IC_RIGHT(ic)), - OP_USES(IC_RIGHT(ic))); - IC_RIGHT(ic)->isaddr = 0; + if (ic->op == JUMPTABLE) { + if (IS_TRUE_SYMOP(IC_JTCOND(ic))) + replaceRegEqvOperand(ic, &IC_JTCOND(ic), 0, 0); + continue; } - if (IC_LEFT(ic) && IS_TRUE_SYMOP(IC_LEFT(ic)) && - isLocalWithoutDef(OP_SYMBOL(IC_LEFT(ic)))) { - werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, - OP_SYMBOL(IC_LEFT(ic))->name); - OP_REQV(IC_LEFT(ic)) = NULL; - OP_SYMBOL(IC_LEFT(ic))->allocreq = 1; + if (ic->op == RECEIVE) { + if (OP_SYMBOL(IC_RESULT(ic))->addrtaken) + OP_SYMBOL(IC_RESULT(ic))->isspilt = 1; } - if (IC_LEFT(ic) && IS_TRUE_SYMOP(IC_LEFT(ic)) && OP_REQV(IC_LEFT(ic))) { - IC_LEFT(ic) = opFromOpWithDU(OP_REQV(IC_LEFT(ic)), OP_DEFS(IC_LEFT(ic)), - OP_USES(IC_LEFT(ic))); - IC_LEFT(ic)->isaddr = 0; + /* general case */ + if (IS_TRUE_SYMOP(IC_RESULT(ic))) { + if (POINTER_SET(ic)) + replaceRegEqvOperand(ic, &IC_RESULT(ic), 1, 1); + else + replaceRegEqvOperand(ic, &IC_RESULT(ic), 0, 0); } + if (IS_TRUE_SYMOP(IC_RIGHT(ic))) + replaceRegEqvOperand(ic, &IC_RIGHT(ic), 1, 0); + if (IS_TRUE_SYMOP(IC_LEFT(ic))) + replaceRegEqvOperand(ic, &IC_LEFT(ic), 1, 0); } } } @@ -1569,6 +1727,8 @@ int killDeadCode(ebbIndex *ebbi) { } /* delete the result */ + if (IC_RESULT(ic)) + bitVectUnSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); IC_RESULT(ic) = NULL; if (volLeft || volRight) { @@ -1586,9 +1746,11 @@ int killDeadCode(ebbIndex *ebbi) { remiCodeFromeBBlock(ebbs[i], ic); /* for the left & right remove the usage */ - if (IS_SYMOP(IC_LEFT(ic))) + if (IS_SYMOP(IC_LEFT(ic))) { + if (OP_SYMBOL(IC_LEFT(ic))->isstrlit) + freeStringSymbol(OP_SYMBOL(IC_LEFT(ic))); bitVectUnSetBit(OP_USES(IC_LEFT(ic)), ic->key); - + } if (IS_SYMOP(IC_RIGHT(ic))) bitVectUnSetBit(OP_USES(IC_RIGHT(ic)), ic->key); } @@ -1645,6 +1807,679 @@ static void discardDeadParamReceives(eBBlock **ebbs, int count) { } } +/* Insert a cast of operand op of ic to type type */ +static void prependCast(iCode *ic, operand *op, sym_link *type, eBBlock *ebb) { + iCode *newic = newiCode(CAST, operandFromLink(type), op); + hTabAddItem(&iCodehTab, newic->key, newic); + + IC_RESULT(newic) = newiTempOperand(type, 0); + bitVectSetBit(OP_USES(op), newic->key); + OP_DEFS(IC_RESULT(newic)) = + bitVectSetBit(OP_DEFS(IC_RESULT(newic)), newic->key); + bitVectUnSetBit(OP_USES(op), ic->key); + OP_USES(IC_RESULT(newic)) = bitVectSetBit(OP_USES(IC_RESULT(newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + + addiCodeToeBBlock(ebb, newic, ic); + + if (isOperandEqual(op, IC_LEFT(ic))) + IC_LEFT(ic) = IC_RESULT(newic); + + if (isOperandEqual(op, IC_RIGHT(ic))) + IC_RIGHT(ic) = IC_RESULT(newic); +} + +/* Insert a cast of result of ic from type type */ +static void appendCast(iCode *ic, sym_link *type, eBBlock *ebb) { + iCode *newic = newiCode(CAST, operandFromLink(operandType(IC_RESULT(ic))), 0); + hTabAddItem(&iCodehTab, newic->key, newic); + + IC_RESULT(newic) = IC_RESULT(ic); + bitVectUnSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); + bitVectSetBit(OP_DEFS(IC_RESULT(ic)), newic->key); + IC_RESULT(ic) = newiTempOperand(type, 0); + IC_RIGHT(newic) = operandFromOperand(IC_RESULT(ic)); + bitVectSetBit(OP_DEFS(IC_RESULT(ic)), ic->key); + bitVectSetBit(OP_USES(IC_RESULT(ic)), newic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock(ebb, newic, ic->next); +} + +/*-----------------------------------------------------------------*/ +/* optimizeOpWidth - reduce operation width. */ +/* Wide arithmetic operations where the result is cast to narrow */ +/* type can be optimized by doing the casts on the operands */ +/*-----------------------------------------------------------------*/ +static int optimizeOpWidth(eBBlock **ebbs, int count) { + int i; + int change = 0; + iCode *ic, *newic; + iCode *uic, *skipuic; + sym_link *nextresulttype; + symbol *sym; + int resultsize, nextresultsize; + + // Wide loop counter + for (i = 0; i < count; i++) { + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + sym_link *newcountertype, *oldcountertype; + const symbol *label; + const iCode *ifx, *inc = 0, *obstacle = 0; + iCode *mul = 0; + bool found = false; + + if (ic->op != LABEL || !ic->next) + continue; + + label = IC_LABEL(ic); + ic = ic->next; + + if (ic->op != '<' || !IS_ITEMP(IC_LEFT(ic)) || + bitVectnBitsOn(OP_DEFS(IC_LEFT(ic))) != 2) + continue; + + oldcountertype = operandType(IC_LEFT(ic)); + if (IS_VOLATILE(oldcountertype)) + continue; + + // Only try to narrow wide counters. + if (!IS_INTEGRAL(oldcountertype) || bitsForType(oldcountertype) <= 8 || + (!SPEC_USIGN( + oldcountertype))) // TODO: Handle signed types as well, maybe even + // transform int to unsigned int? + continue; + + ifx = ifxForOp(IC_RESULT(ic), ic); + + if (!ifx || IC_TRUE(ifx) || i + 1 >= count) + continue; + + /* For now we handle only loops that have no complex control flow inside + them and where the loop is entered and left through ifx only */ + for (uic = ebbs[i + 1]->sch; uic; uic = uic->next) { + if (uic->op == GOTO && IC_LABEL(uic) == label) + break; + + if (!obstacle && + (uic->op == CALL || uic->op == PCALL || uic->op == IFX || + uic->op == LABEL || uic->op == GOTO && IC_LABEL(uic) != label || + uic->op == INLINEASM)) { + obstacle = uic; + break; + } + } + + // TODO: Proceed despite obstacle, but only consider array accesses before + // obstacle. + if (obstacle || !uic || uic->op != GOTO || IC_LABEL(uic) != label) + continue; + + const bitVect *uses; + int bit; + + uses = bitVectCopy(OP_USES(IC_LEFT(ic))); + for (bit = bitVectFirstBit(uses); bitVectnBitsOn(uses); + bitVectUnSetBit(uses, bit), bit = bitVectFirstBit(uses)) { + operand *prevresult = IC_LEFT(ic); + operand *mulotherop = 0; + iCode *mul_candidate = 0; + uic = hTabItemWithKey(iCodehTab, bit); + + if (!uic) { + /* This iCode has been deleted but is still */ + /* referenced. This shouldn't happen if everything */ + /* else is managing OP_USES properly, but better */ + /* to ignore the problem than crash. */ + // printf ("%s used in iCode %d, but iCode missing\n", OP_SYMBOL + // (IC_LEFT (ic))->name, bit); + continue; + } + + if (uic->op == '+' && IS_OP_LITERAL(IC_RIGHT(uic)) && + operandLitValue(IC_RIGHT(uic)) == 1 && + isOperandEqual(IC_LEFT(uic), IC_LEFT(ic))) { + inc = uic; + continue; + } + + if (uic->op != CAST && uic->op != '=' && uic->op != '+' && + uic->op != '*' && uic->op != '-' && uic->op != LEFT_OP && + uic->op != RIGHT_OP && uic->op != '<') { + found = false; + break; + } + + if (uic && uic->op == '*') { + mulotherop = isOperandEqual(IC_LEFT(ic), IC_LEFT(uic)) ? IC_RIGHT(uic) + : IC_LEFT(uic); + if (isOperandEqual(IC_RIGHT(ic), mulotherop)) { + mul_candidate = uic; + uic = hTabItemWithKey(iCodehTab, + bitVectFirstBit(OP_USES(IC_RESULT(uic)))); + } + } + + for (int i = 0; + i < 8 && uic && + (uic->op == CAST && + bitsForType(operandType(IC_RESULT(uic))) >= 16 || + uic->op == '=' || uic->op == '+' || uic->op == LEFT_OP || + uic->op == '*' && IS_OP_LITERAL(IC_RIGHT(uic)) && + operandLitValue(IC_RIGHT(uic)) >= 1); + i++) { + prevresult = IC_RESULT(uic); + uic = hTabItemWithKey(iCodehTab, + bitVectFirstBit(OP_USES(IC_RESULT(uic)))); + } + + if (!uic) + continue; + + // Use as array index? + if (uic->op == GET_VALUE_AT_ADDRESS || + POINTER_SET(uic) && isOperandEqual(IC_RESULT(uic), prevresult)) { + found = true; + if (mul_candidate) + mul = mul_candidate; + } + } + + if (!found || !inc) + continue; + + /* All backends (except ds390 / ds400) have an array size limit smaller + than 2^16. Thus if the loop counter ever goes outside the range of a + 16-bit type, the array access would result in undefined behaviour. We + can thus replace the loop counter by a 16-bit type. If we found a + squaring multiplication, we can even use an 8-bit type*/ + if (bitsForType(oldcountertype) <= 16 && !mul) + continue; + + newcountertype = mul ? newCharLink() : newIntLink(); + SPEC_USIGN(newcountertype) = 1; + OP_SYMBOL(IC_LEFT(ic))->type = newcountertype; + OP_SYMBOL(IC_RESULT(inc))->type = newcountertype; + + uses = bitVectCopy(OP_USES(IC_LEFT(ic))); + for (bit = bitVectFirstBit(uses); bitVectnBitsOn(uses); + bitVectUnSetBit(uses, bit), bit = bitVectFirstBit(uses)) { + uic = hTabItemWithKey(iCodehTab, bit); + + if (uic == inc || uic == mul) + continue; + if (uic->op == CAST) + continue; + if (uic->key == ic->key) + continue; + if (uic->op == '=') { + uic->op = CAST; + continue; + } + + // Need to insert cast. + prependCast(uic, IC_LEFT(ic), oldcountertype, ebbs[i + 1]); + } + + // Insert cast for comparison. + if (IS_OP_LITERAL(IC_RIGHT(ic))) + IC_RIGHT(ic) = operandFromValue( + valCastLiteral(newcountertype, operandLitValue(IC_RIGHT(ic)), + operandLitValue(IC_RIGHT(ic)))); + else + prependCast(ic, IC_RIGHT(ic), newcountertype, ebbs[i]); + + // Bonus: Can narrow a multiplication in the loop. + if (mul) { + prependCast(mul, IC_LEFT(mul), newcountertype, ebbs[i + 1]); + prependCast(mul, IC_RIGHT(mul), newcountertype, ebbs[i + 1]); + nextresulttype = newIntLink(); + SPEC_USIGN(nextresulttype) = 1; + appendCast(mul, nextresulttype, ebbs[i + 1]); + } + } + } + + /* long and long long multiplications where operands are unsigned char due to + * bitwise and */ + for (i = 0; i < count; i++) + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (ic->op == '*' && IC_RESULT(ic) && IS_ITEMP(IC_RESULT(ic))) { + operand *left = IC_LEFT(ic); + operand *right = IC_RIGHT(ic); + sym_link *resulttype = operandType(IC_RESULT(ic)); + + if (!IS_INTEGRAL(resulttype) || bitsForType(resulttype) <= 16 || + !(IS_ITEMP(left) || IS_OP_LITERAL(left)) || + !(IS_ITEMP(right) || IS_OP_LITERAL(right))) + continue; + + if (IS_ITEMP(left) && bitVectnBitsOn(OP_DEFS(left)) != 1 || + IS_ITEMP(right) && bitVectnBitsOn(OP_DEFS(right)) != 1) + continue; + + iCode *lic = + IS_ITEMP(left) + ? hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(left))) + : 0; + iCode *ric = + IS_ITEMP(right) + ? hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(right))) + : 0; + + if (lic) { + if (lic->op != BITWISEAND || + !IS_OP_LITERAL(IC_LEFT(lic)) && !IS_OP_LITERAL(IC_RIGHT(lic))) + continue; + + unsigned long litval = operandLitValue( + IS_OP_LITERAL(IC_LEFT(lic)) ? IC_LEFT(lic) : IC_RIGHT(lic)); + + if (litval > 0x7f) + continue; + } else if (operandLitValue(left) > 0x7f) + continue; + + if (ric) { + if (ric->op != BITWISEAND || + !IS_OP_LITERAL(IC_LEFT(ric)) && !IS_OP_LITERAL(IC_RIGHT(ric))) + continue; + + unsigned long litval = operandLitValue( + IS_OP_LITERAL(IC_LEFT(ric)) ? IC_LEFT(ric) : IC_RIGHT(ric)); + + if (litval > 0x7f) + continue; + } else if (operandLitValue(right) > 0x7f) + continue; + + // Now replace the wide multiplication by 8x8->16 multiplication and + // insert casts. + + if (lic) { + newic = newiCode(CAST, operandFromLink(newCharLink()), left); + hTabAddItem(&iCodehTab, newic->key, newic); + IC_RESULT(newic) = newiTempOperand(newCharLink(), 0); + IC_LEFT(ic) = operandFromOperand(IC_RESULT(newic)); + bitVectUnSetBit(OP_USES(left), ic->key); + bitVectSetBit(OP_USES(left), newic->key); + OP_DEFS(IC_RESULT(newic)) = + bitVectSetBit(OP_DEFS(IC_RESULT(newic)), newic->key); + OP_USES(IC_RESULT(newic)) = + bitVectSetBit(OP_USES(IC_RESULT(newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock(ebbs[i], newic, ic); + } else + IC_LEFT(ic) = operandFromValue( + valCastLiteral(newCharLink(), operandLitValue(IC_LEFT(ic)), + operandLitValue(IC_LEFT(ic)))); + + if (ric) { + newic = newiCode(CAST, operandFromLink(newCharLink()), right); + hTabAddItem(&iCodehTab, newic->key, newic); + IC_RESULT(newic) = newiTempOperand(newCharLink(), 0); + IC_RIGHT(ic) = operandFromOperand(IC_RESULT(newic)); + bitVectUnSetBit(OP_USES(right), ic->key); + bitVectSetBit(OP_USES(right), newic->key); + OP_DEFS(IC_RESULT(newic)) = + bitVectSetBit(OP_DEFS(IC_RESULT(newic)), newic->key); + OP_USES(IC_RESULT(newic)) = + bitVectSetBit(OP_USES(IC_RESULT(newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock(ebbs[i], newic, ic); + } else + IC_LEFT(ic) = operandFromValue( + valCastLiteral(newCharLink(), operandLitValue(IC_LEFT(ic)), + operandLitValue(IC_LEFT(ic)))); + + // Insert cast on result + nextresulttype = newIntLink(); + SPEC_USIGN(nextresulttype) = 1; + appendCast(ic, nextresulttype, ebbs[i]); + } + + // Operation followed by cast + for (i = 0; i < count; i++) { + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS || + ic->op == '*' || ic->op == LEFT_OP || ic->op == RIGHT_OP || + ic->op == BITWISEAND || ic->op == '|' || ic->op == CAST) && + IC_RESULT(ic) && IS_ITEMP(IC_RESULT(ic))) { + sym_link *resulttype = operandType(IC_RESULT(ic)); + + if (!IS_INTEGRAL(resulttype) || + ic->op != CAST && + !(IS_SYMOP(IC_LEFT(ic)) || IS_OP_LITERAL(IC_LEFT(ic))) || + !(IS_SYMOP(IC_RIGHT(ic)) || IS_OP_LITERAL(IC_RIGHT(ic)) || + ic->op == UNARYMINUS)) + continue; + + resultsize = bitsForType(resulttype); + + /* There must be only one use of this first result */ + if (bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) != 1 || + bitVectnBitsOn(OP_USES(IC_RESULT(ic))) != 1) + continue; + + uic = + hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_USES(IC_RESULT(ic)))); + + if (!uic) + continue; + + /* Skip over assignment */ + skipuic = NULL; + if (uic->op == '=' && IS_ITEMP(IC_RESULT(uic)) && + bitVectnBitsOn(OP_DEFS(IC_RESULT(uic))) == 1 && + bitVectnBitsOn(OP_USES(IC_RESULT(ic))) == 1 && + bitVectnBitsOn(OP_USES(IC_RESULT(uic))) == 1 && + compareType(operandType(IC_RESULT(ic)), + operandType(IC_RESULT(uic))) == 1) { + skipuic = uic; + uic = hTabItemWithKey(iCodehTab, + bitVectFirstBit(OP_USES(IC_RESULT(uic)))); + } + + /* Try to handle a few cases where the result has multiple uses */ + else if (ic->op == '*' && + bitsForType(operandType(IC_RESULT(ic))) > 16 && + uic->op == '=' && + bitVectnBitsOn(OP_DEFS(IC_RESULT(uic))) == 1 && + bitVectnBitsOn(OP_USES(IC_RESULT(ic))) == 1 && + bitVectnBitsOn(OP_USES(IC_RESULT(uic))) > 1 && + compareType(operandType(IC_RESULT(ic)), + operandType(IC_RESULT(uic))) == 1) { + bool ok = true; + const bitVect *uses; + int bit; + + uses = bitVectCopy(OP_USES(IC_RESULT(uic))); + for (bit = bitVectFirstBit(uses); bitVectnBitsOn(uses); + bitVectUnSetBit(uses, bit), bit = bitVectFirstBit(uses)) { + iCode *uuic = hTabItemWithKey(iCodehTab, bit); + if (uuic->op != CAST || + bitsForType(operandType(IC_RESULT(uuic))) > 16 || + IS_BOOLEAN(operandType(IC_RESULT(uuic)))) { + ok = false; + break; + } + } + + if (!ok) + continue; + + nextresulttype = newIntLink(); + SPEC_USIGN(nextresulttype) = 1; + sym = OP_SYMBOL(IC_RESULT(uic)); + sym->type = nextresulttype; + + nextresulttype = newIntLink(); + SPEC_USIGN(nextresulttype) = 1; + goto optimize; + } + + if (uic->op != CAST && uic->op != '+' && uic->op != LEFT_OP && + uic->op != RIGHT_OP) + continue; + + /* Special handling since we might need more bits in the operand than in + * the result */ + if (ic->op == RIGHT_OP) { + int shiftbits, resultbits; + + if (!IS_OP_LITERAL(IC_RIGHT(ic))) + continue; + + shiftbits = (int)operandLitValue(IC_RIGHT(ic)); + resultbits = bitsForType(operandType(IC_RESULT(uic))); + + if (resultbits + shiftbits > 16) + continue; + else if (resultbits + shiftbits > 8) + nextresulttype = newIntLink(); + else + nextresulttype = newCharLink(); + SPEC_USIGN(nextresulttype) = 1; + } + /* It must be a cast to another integer type that */ + /* has fewer bits */ + else if (uic->op == LEFT_OP || uic->op == RIGHT_OP) { + /* Since shifting by the width of an operand or more is undefined + behaviour, and no type is wider than 256 bits, + we can optimize when the result is used as right operand to a + shift. */ + if (!isOperandEqual(IC_RESULT(ic), IC_RIGHT(uic)) || + isOperandEqual(IC_RESULT(ic), IC_LEFT(uic))) + continue; + + nextresulttype = newCharLink(); + } else { + nextresulttype = operandType(IC_RESULT(uic)); + if (!IS_INTEGRAL(nextresulttype) && + !(IS_PTR(nextresulttype) && NEARPTRSIZE == 2)) + continue; + + if (IS_PTR(nextresulttype)) { + nextresulttype = newIntLink(); + SPEC_USIGN(nextresulttype) = 1; + } else + nextresulttype = copyLinkChain(nextresulttype); + } + + nextresultsize = bitsForType(nextresulttype); + if (nextresultsize >= resultsize) + continue; + /* Cast to bool and bool-like types must be preserved to ensure that all + * nonzero values are correctly cast to true */ + if (uic->op == CAST && IS_BOOLEAN(nextresulttype)) + continue; + + optimize: + /* Make op result narrower */ + sym = OP_SYMBOL(IC_RESULT(ic)); + sym->type = nextresulttype; + + /* Insert casts on operands */ + if (ic->op != CAST) { + sym_link *clefttype = copyLinkChain(nextresulttype); + SPEC_VOLATILE(clefttype) = IS_OP_VOLATILE(IC_LEFT(ic)); + sym_link *crighttype = copyLinkChain(nextresulttype); + SPEC_VOLATILE(crighttype) = IS_OP_VOLATILE(IC_RIGHT(ic)); + + if (IS_SYMOP(IC_LEFT(ic))) { + newic = newiCode(CAST, operandFromLink(clefttype), IC_LEFT(ic)); + hTabAddItem(&iCodehTab, newic->key, newic); + bitVectSetBit(OP_USES(IC_LEFT(ic)), newic->key); + IC_RESULT(newic) = newiTempOperand(clefttype, 0); + OP_DEFS(IC_RESULT(newic)) = + bitVectSetBit(OP_DEFS(IC_RESULT(newic)), newic->key); + bitVectUnSetBit(OP_USES(IC_LEFT(ic)), ic->key); + IC_LEFT(ic) = operandFromOperand(IC_RESULT(newic)); + OP_USES(IC_LEFT(ic)) = bitVectSetBit(OP_USES(IC_LEFT(ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock(ebbs[i], newic, ic); + } else { + wassert(IS_OP_LITERAL(IC_LEFT(ic))); + IC_LEFT(ic) = operandFromValue( + valCastLiteral(clefttype, operandLitValue(IC_LEFT(ic)), + operandLitValue(IC_LEFT(ic)))); + } + if (ic->op != LEFT_OP && IS_SYMOP(IC_RIGHT(ic))) { + newic = newiCode(CAST, operandFromLink(crighttype), IC_RIGHT(ic)); + hTabAddItem(&iCodehTab, newic->key, newic); + bitVectSetBit(OP_USES(IC_RIGHT(ic)), newic->key); + IC_RESULT(newic) = newiTempOperand(crighttype, 0); + OP_DEFS(IC_RESULT(newic)) = + bitVectSetBit(OP_DEFS(IC_RESULT(newic)), newic->key); + bitVectUnSetBit(OP_USES(IC_RIGHT(ic)), ic->key); + IC_RIGHT(ic) = operandFromOperand(IC_RESULT(newic)); + OP_USES(IC_RIGHT(ic)) = + bitVectSetBit(OP_USES(IC_RIGHT(ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock(ebbs[i], newic, ic); + } else if (ic->op != LEFT_OP && ic->op != UNARYMINUS) { + wassert(IS_OP_LITERAL(IC_RIGHT(ic))); + IC_RIGHT(ic) = operandFromValue( + valCastLiteral(crighttype, operandLitValue(IC_RIGHT(ic)), + operandLitValue(IC_RIGHT(ic)))); + } + } + if (uic->op == CAST && ic->op != RIGHT_OP) { + uic->op = '='; + if (skipuic) { + bitVectUnSetBit(OP_USES(IC_RIGHT(uic)), uic->key); + IC_RIGHT(uic) = IC_RIGHT(skipuic); + OP_USES(IC_RIGHT(uic)) = + bitVectSetBit(OP_USES(IC_RIGHT(uic)), uic->key); + } + } + change++; + } + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* Go back a chain of assigments / casts to try to find a string */ +/* literal symbol that op really is. */ +/*-----------------------------------------------------------------*/ +static symbol *findStrLitDef(operand *op, iCode **def) { + for (;;) { + if (!IS_ITEMP(op)) + return (0); + + if (bitVectnBitsOn(OP_DEFS(op)) != 1) + return (0); + + iCode *dic = hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(op))); + + wassert(dic); + + if (dic->op == ADDRESS_OF) { + if (def) + *def = dic; + symbol *sym = OP_SYMBOL(IC_LEFT(dic)); + return (sym->isstrlit ? sym : 0); + } + + if (dic->op != '=' && dic->op != CAST) + return (0); + + op = IC_RIGHT(dic); + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeStdLibCall - optimize calls to standard library. */ +/* for now we just merge adjacent calls to puts() */ +/*-----------------------------------------------------------------*/ +static void optimizeStdLibCall(eBBlock **ebbs, int count) { + iCode *ic, *nic, *ndic; + symbol *strsym, *nstrsym, *cstrsym; + sym_link *strlink, *nstrlink; + size_t replacecost; + + for (int i = 0; i < count; i++) { + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + // Look for call to puts(). + if (ic->op != CALL || !ic->prev || + ic->prev->op != IPUSH && ic->prev->op != SEND) + continue; + if (!IS_SYMOP(IC_LEFT(ic)) || !OP_SYMBOL(IC_LEFT(ic))->rname || + strcmp(OP_SYMBOL(IC_LEFT(ic))->rname, "_puts")) + continue; + + // Look for following call to puts(). + for (nic = ic->next; nic; nic = nic->next) { + if (nic->op == '=' && !POINTER_SET(ic) || nic->op == CAST) { + if (!IS_ITEMP(IC_RESULT(nic))) + break; + if (IS_OP_VOLATILE(IC_RIGHT(nic))) + break; + } else if (nic->op == ADDRESS_OF) { + if (!IS_ITEMP(IC_RESULT(nic))) + break; + } else if (nic->op == IPUSH || nic->op == SEND) { + if (IS_OP_VOLATILE(IC_LEFT(nic))) + break; + } else // Todo: Handle more to make the optimization more general. + break; + } + if (!nic || nic->op != CALL || + nic->prev->op != IPUSH && nic->prev->op != SEND) + continue; + if (!IS_SYMOP(IC_LEFT(nic)) || !OP_SYMBOL(IC_LEFT(nic))->rname || + strcmp(OP_SYMBOL(IC_LEFT(nic))->rname, "_puts")) + continue; + + // Check that the return values are unused + if (IC_RESULT(ic) && + (!IS_ITEMP(IC_RESULT(ic)) || bitVectnBitsOn(OP_USES(IC_RESULT(ic))))) + continue; + if (IC_RESULT(nic) && (!IS_ITEMP(IC_RESULT(nic)) || + bitVectnBitsOn(OP_USES(IC_RESULT(nic))))) + continue; + + // Chek that their parameters are string literals + strsym = findStrLitDef(IC_LEFT(ic->prev), 0); + nstrsym = findStrLitDef(IC_LEFT(nic->prev), &ndic); + if (!strsym || !nstrsym) + continue; + strlink = strsym->etype; + nstrlink = nstrsym->etype; + + // Calculate the cost of doing the replacement in bytes of string literal + replacecost = 1; // For '\n' + if (strsym->isstrlit > 1) + replacecost += strlen(SPEC_CVAL(strlink).v_char); + if (nstrsym->isstrlit > 1) + replacecost += strlen(SPEC_CVAL(nstrlink).v_char); + + // Doing the replacement saves at least 6 bytes of call overhead (assuming + // pointers are 16 bits). + if (replacecost > 7 - optimize.codeSize + 4 * optimize.codeSpeed) + continue; + + // Combine strings + struct dbuf_s dbuf; + dbuf_init(&dbuf, 3); + dbuf_append_str(&dbuf, SPEC_CVAL(strlink).v_char); + dbuf_append_str(&dbuf, "\n"); + dbuf_append_str(&dbuf, SPEC_CVAL(nstrlink).v_char); + cstrsym = stringToSymbol( + rawStrVal(dbuf_c_str(&dbuf), dbuf_get_length(&dbuf) + 1)) + ->sym; + freeStringSymbol(nstrsym); + dbuf_destroy(&dbuf); + + // Make second call print the combined string (which allows further + // optimization with subsequent calls) + IC_LEFT(ndic)->key = cstrsym->key; + IC_LEFT(ndic)->svt.symOperand = cstrsym; + + // Change unused call to assignments to self to mark it for dead-code + // elimination. + bitVectSetBit(OP_USES(IC_LEFT(ic->prev)), ic->key); + bitVectSetBit(OP_DEFS(IC_LEFT(ic->prev)), ic->prev->key); + ic->op = '='; + IC_RESULT(ic) = IC_LEFT(ic->prev); + IC_RIGHT(ic) = IC_LEFT(ic->prev); + IC_LEFT(ic) = 0; + ic->prev->op = '='; + IC_RESULT(ic->prev) = IC_LEFT(ic->prev); + IC_RIGHT(ic->prev) = IC_LEFT(ic->prev); + IC_LEFT(ic->prev) = 0; + } + } +} + /*-----------------------------------------------------------------*/ /* optimizeCastCast - remove unneeded intermediate casts. */ /* Integer promotion may cast (un)signed char to int and then */ @@ -1681,41 +2516,60 @@ static void optimizeCastCast(eBBlock **ebbs, int count) { continue; /* There must be only one use of this first result */ - if (bitVectnBitsOn(OP_USES(IC_RESULT(ic))) != 1) + if (bitVectnBitsOn(OP_USES(IC_RESULT(ic))) != 1 || + bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) != 1) continue; - /* This use must be a second cast */ uic = hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_USES(IC_RESULT(ic)))); - if (!uic || uic->op != CAST) + if (!uic || (uic->op != CAST && uic->op != BITWISEAND)) continue; - /* It must be a cast to another integer type that */ - /* has no loss of bits */ type3 = operandType(IC_RESULT(uic)); - if (!IS_INTEGRAL(type3)) - continue; - size3 = bitsForType(type3); - if (size3 < size2) - continue; - /* If they are the same size, they must have the same signedness */ - if (size3 == size2 && SPEC_USIGN(type3) != SPEC_USIGN(type2)) - continue; - - /* The signedness between the first and last types */ - /* must match */ - if (SPEC_USIGN(type3) != SPEC_USIGN(type1)) - continue; /* Cast to bool must be preserved to ensure that all nonzero values are * correctly cast to true */ if (SPEC_NOUN(type2) == V_BOOL && SPEC_NOUN(type3) != V_BOOL) continue; + /* Special case: Second use is a bit test */ + if (uic->op == BITWISEAND && IS_OP_LITERAL(IC_RIGHT(uic)) && + ifxForOp(IC_RESULT(uic), uic)) { + unsigned long long mask = operandLitValue(IC_RIGHT(uic)); + + /* Signed cast might set bits above the width of type1 */ + if (!SPEC_USIGN(type1) && (mask >> (bitsForType(type1)))) + continue; + + IC_RIGHT(uic) = operandFromValue( + valCastLiteral(type1, operandLitValue(IC_RIGHT(uic)), + operandLitValue(IC_RIGHT(uic)))); + } else if (uic->op == + CAST) /* Otherwise this use must be a second cast */ + { + /* It must be a cast to another integer type that */ + /* has no loss of bits */ + type3 = operandType(IC_RESULT(uic)); + if (!IS_INTEGRAL(type3)) + continue; + size3 = bitsForType(type3); + if (size3 < size1) + continue; + /* If they are the same size, they must have the same signedness */ + if (size3 == size2 && SPEC_USIGN(type3) != SPEC_USIGN(type2)) + continue; + + /* The signedness between the first and last types must match */ + if (SPEC_USIGN(type3) != SPEC_USIGN(type1)) + continue; + } else + continue; + /* Change the first cast to a simple assignment and */ /* let the second cast do all the work */ ic->op = '='; IC_LEFT(ic) = NULL; + sym = OP_SYMBOL(IC_RESULT(ic)); sym->type = copyLinkChain(type1); sym->etype = getSpec(sym->type); @@ -1724,6 +2578,47 @@ static void optimizeCastCast(eBBlock **ebbs, int count) { } } +/*-----------------------------------------------------------------*/ +/* optimizeNegation - remove unneeded intermediate negation */ +/*-----------------------------------------------------------------*/ +static void optimizeNegation(eBBlock **ebbs, int count) { + int i; + iCode *ic; + iCode *uic; + + for (i = 0; i < count; i++) { + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + if (ic->op == '!' && IC_RESULT(ic) && IS_ITEMP(IC_RESULT(ic))) { + /* There must be only one use of this first result */ + if (bitVectnBitsOn(OP_USES(IC_RESULT(ic))) != 1) + continue; + + /* This use must be an ifx */ + uic = + hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_USES(IC_RESULT(ic)))); + if (!uic) + continue; + /* Todo: Optimize case where use is another negation */ + else if (uic->op == IFX) /* Remove negation by inverting jump targets */ + { + IC_LEFT(uic) = IC_LEFT(ic); + IC_LEFT(ic) = 0; + IC_RIGHT(ic) = IC_RESULT(ic); + ic->op = '='; + + if (IC_TRUE(uic)) { + IC_FALSE(uic) = IC_TRUE(uic); + IC_TRUE(uic) = 0; + } else { + IC_TRUE(uic) = IC_FALSE(uic); + IC_FALSE(uic) = 0; + } + } + } + } + } +} + /* Fold pointer addition into offset of ADDRESS_OF. */ static void offsetFoldGet(eBBlock **ebbs, int count) { int i; @@ -1817,6 +2712,127 @@ static void offsetFoldUse(eBBlock **ebbs, int count) { } } +/*-----------------------------------------------------------------*/ +/* guessCounts - Guess execution counts for iCodes */ +/* Needs ic->seq assigned (typically done by computeLiveRanges() */ +/*-----------------------------------------------------------------*/ +void guessCounts(iCode *start_ic, ebbIndex *ebbi) { + iCode *ic; + int i; + bool needprop; + + for (ic = start_ic; ic; ic = ic->next) + ic->count = 0; + start_ic->pcount = 1.0f; + needprop = TRUE; + + for (i = 0; needprop && i < 24; i++) // 24 is an arbitrary limit to reduce + // runtime at the cost of accuracy. + { + needprop = FALSE; + for (ic = start_ic; ic; ic = ic->next) { + if (ic->pcount <= 0.01) // 0.01 is an arbitrary limit to reduce runtime at + // the cost of accuracy. + continue; + + ic->count += ic->pcount; + + if (ic->op == GOTO) { + iCode *target = hTabItemWithKey(labelDef, IC_LABEL(ic)->key); + target->pcount += ic->pcount; + needprop = TRUE; + } else if (ic->op == IFX) // Use a classic, simple branch prediction. + // Works well for typical loops. + { + iCode *target = hTabItemWithKey( + labelDef, (IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->key); + if (target->seq >= ic->seq) { + target->pcount += ic->pcount / 4; + if (ic->next) + ic->next->pcount += ic->pcount * 3 / 4; + } else { + target->pcount += ic->pcount * 3 / 4; + if (ic->next) + ic->next->pcount += ic->pcount / 4; + } + needprop = TRUE; + } else if (ic->op == JUMPTABLE) { + symbol *label; + int n = elementsInSet(IC_JTLABELS(ic)); + + for (label = setFirstItem(IC_JTLABELS(ic)); label; + label = setNextItem(IC_JTLABELS(ic))) { + iCode *target = hTabItemWithKey(labelDef, label->key); + target->pcount += ic->pcount / n; + } + needprop = TRUE; + } else if (ic->op == CALL && IS_SYMOP(IC_LEFT(ic)) && + IFFUNC_ISNORETURN(OP_SYMBOL(IC_LEFT(ic))->type)) + ; + else if (ic->next) + ic->next->pcount += ic->pcount; + ic->pcount = 0.0f; + } + } +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating a downcast. */ +/*-----------------------------------------------------------------*/ +static int narrowRead(iCode *ic, operand **opp, eBBlock *ebp) { + iCode *dic; + operand *op = *opp; + + if (ic->op != CAST || !IS_ITEMP(op)) + return 0; + + if (bitVectnBitsOn(OP_USES(op)) != 1 || bitVectnBitsOn(OP_DEFS(op)) != 1) + return 0; + + // get the definition + if (!(dic = hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(op))))) + return 0; + + // found the definition now check if it is local + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; + + // for now handle pointer reads only + if (dic->op != GET_VALUE_AT_ADDRESS || + IS_VOLATILE(operandType(IC_LEFT(dic))->next)) + return 0; + + sym_link *resulttype = operandType(IC_RESULT(ic)); + sym_link *righttype = operandType(IC_RIGHT(ic)); + + if (IS_BOOL(resulttype) || getSize(resulttype) >= getSize(righttype)) + return 0; + + // Narrow read + if (!port->little_endian) { + int offset = getSize(righttype) - getSize(resulttype); + IC_RIGHT(dic) = operandFromLit(operandLitValue(IC_RIGHT(dic)) + offset); + } + OP_SYMBOL(IC_RESULT(dic))->type = resulttype; + ic->op = '='; + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating downcasts. */ +/*-----------------------------------------------------------------*/ +static void narrowReads(ebbIndex *ebbi) { + for (int i = 0; i < ebbi->count; i++) { + eBBlock **ebbs = ebbi->bbOrder; + eBBlock *ebp = ebbs[i]; + + for (iCode *ic = ebp->sch; ic; ic = ic->next) + if (ic->op == CAST) + narrowRead(ic, &(IC_RIGHT(ic)), ebp); + } +} + /*-----------------------------------------------------------------*/ /* eBBlockFromiCode - creates extended basic blocks from iCode */ /* will return an array of eBBlock pointers */ @@ -1867,7 +2883,13 @@ eBBlock **eBBlockFromiCode(iCode *ic) { if (options.dump_i_code) dumpEbbsToFileExt(DUMP_RAW1, ebbi); + if (!optimize.noStdLibCall) + optimizeStdLibCall(ebbi->bbOrder, ebbi->count); + optimizeCastCast(ebbi->bbOrder, ebbi->count); + while (optimizeOpWidth(ebbi->bbOrder, ebbi->count)) + optimizeCastCast(ebbi->bbOrder, ebbi->count); + optimizeNegation(ebbi->bbOrder, ebbi->count); /* Burn the corpses, so the dead may rest in peace, safe from cse necromancy */ @@ -1938,17 +2960,21 @@ eBBlock **eBBlockFromiCode(iCode *ic) { computeControlFlow(ebbi); loops = createLoopRegions(ebbi); computeDataFlow(ebbi); - computeLiveRanges(ebbi->bbOrder, ebbi->count, FALSE); + computeLiveRanges(ebbi->bbOrder, ebbi->count, TRUE); + while (optimizeOpWidth(ebbi->bbOrder, ebbi->count)) + optimizeCastCast(ebbi->bbOrder, ebbi->count); adjustIChain(ebbi->bbOrder, ebbi->count); ic = iCodeLabelOptimize(iCodeFromeBBlock(ebbi->bbOrder, ebbi->count)); - if (optimize.lospre) /* Todo: enable for other ports. */ - { + shortenLiveRanges(ic, ebbi); + guessCounts(ic, ebbi); + if (optimize.lospre) { lospre(ic, ebbi); if (options.dump_i_code) dumpEbbsToFileExt(DUMP_LOSPRE, ebbi); /* GCSE, lospre and maybe other optimizations sometimes create temporaries * that have non-connected live ranges, which is bad. Split them. */ + freeeBBlockData(ebbi); ebbi = iCodeBreakDown(ic); computeControlFlow(ebbi); loops = createLoopRegions(ebbi); @@ -1961,6 +2987,7 @@ eBBlock **eBBlockFromiCode(iCode *ic) { /* Break down again and redo some steps to not confuse live range analysis * later. */ + freeeBBlockData(ebbi); ebbi = iCodeBreakDown(ic); computeControlFlow(ebbi); loops = createLoopRegions(ebbi); @@ -1989,6 +3016,7 @@ eBBlock **eBBlockFromiCode(iCode *ic) { values or very weird control-flow graphs */ /* Break down again and redo some steps to not confuse live range analysis. */ + freeeBBlockData(ebbi); ebbi = iCodeBreakDown(ic); computeControlFlow(ebbi); loops = createLoopRegions(ebbi); @@ -2025,6 +3053,24 @@ eBBlock **eBBlockFromiCode(iCode *ic) { /* miscelaneous optimizations */ miscOpt(ebbi->bbOrder, ebbi->count); + /* Split any live-ranges that became non-connected in dead code elimination. + */ + change = 0; + do { + recomputeLiveRanges(ebbi->bbOrder, ebbi->count, FALSE); + adjustIChain(ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize(iCodeFromeBBlock(ebbi->bbOrder, ebbi->count)); + change = separateLiveRanges(ic, ebbi); + freeeBBlockData(ebbi); + ebbi = iCodeBreakDown(ic); + computeControlFlow(ebbi); + loops = createLoopRegions(ebbi); + computeDataFlow(ebbi); + } while (change); + killDeadCode( + ebbi); /* iCodeLabelOptimize() above might result in dead code, when both + branches of an ifx go to the same destination. */ + /* compute the live ranges */ recomputeLiveRanges(ebbi->bbOrder, ebbi->count, TRUE); @@ -2036,11 +3082,14 @@ eBBlock **eBBlockFromiCode(iCode *ic) { */ discardDeadParamReceives(ebbi->bbOrder, ebbi->count); + narrowReads(ebbi); + /* allocate registers & generate code */ port->assignRegisters(ebbi); /* throw away blocks */ setToNull((void *)&graphEdges); + freeeBBlockData(ebbi); return NULL; } diff --git a/src/SDCCopt.h b/src/SDCCopt.h index 9eb97ad71..202550f4a 100644 --- a/src/SDCCopt.h +++ b/src/SDCCopt.h @@ -37,5 +37,6 @@ int switchAddressSpacesOptimally(iCode *ic, ebbIndex *ebbi); const symbol *getAddrspaceiCode(const iCode *ic); void switchAddressSpaceAt(iCode *ic, const symbol *const addrspace); bool isPowerOf2(unsigned long val); +void guessCounts(iCode *start_ic, ebbIndex *ebbi); #endif diff --git a/src/SDCCpeeph.c b/src/SDCCpeeph.c index eb7a46c34..da27aef17 100644 --- a/src/SDCCpeeph.c +++ b/src/SDCCpeeph.c @@ -42,7 +42,7 @@ static int hashSymbolName(const char *name); static void buildLabelRefCountHash(lineNode *head); static void bindVar(int key, char **s, hTab **vtab); -static bool matchLine(char *, char *, hTab **); +static bool matchLine(char *, const char *, hTab **); #define FBYNAME(x) \ static int x(hTab *vars, lineNode *currPl, lineNode *endPl, lineNode *head, \ @@ -119,12 +119,9 @@ static int pcDistance(lineNode *cpos, char *lbl, bool back) { } /*-----------------------------------------------------------------*/ -/* portIsDS390 - return true if port is DS390 */ +/* flat24bitMode - will check to see if we are in flat24 mode */ /*-----------------------------------------------------------------*/ -FBYNAME(portIsDS390) { - return ((strcmp(port->target, "ds390") == 0) || - (strcmp(port->target, "ds400") == 0)); -} +FBYNAME(flat24bitMode) { return (options.model == MODEL_FLAT24); } /*-----------------------------------------------------------------*/ /* xramMovcOption - check if using movc to read xram */ @@ -276,7 +273,7 @@ FBYNAME(labelIsReturnOnly) { if (!pl) return FALSE; /* did not find the label */ pl = pl->next; - while (pl && (pl->isDebug || pl->isComment)) + while (pl && (pl->isDebug || pl->isComment || pl->isLabel)) pl = pl->next; if (!pl || !pl->line || pl->isDebug) return FALSE; /* next line not valid */ @@ -299,6 +296,7 @@ FBYNAME(labelIsUncondJump) { const char *label; char *p, *q; const lineNode *pl; + bool found = FALSE; int len; char *jpInst = NULL; char *jpInst2 = NULL; @@ -308,20 +306,39 @@ FBYNAME(labelIsUncondJump) { return FALSE; len = strlen(label); - for (pl = currPl; pl; pl = pl->next) { + for (pl = currPl; pl; pl = pl->prev) { if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel) { - if (strncmp(pl->line, label, len) == 0) + if (strncmp(pl->line, label, len) == 0) { + found = TRUE; break; /* Found Label */ + } if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) || !ISCHARDIGIT(*(pl->line + 1)) || !ISCHARDIGIT(*(pl->line + 2)) || !ISCHARDIGIT(*(pl->line + 3)) || !ISCHARDIGIT(*(pl->line + 4)) || *(pl->line + 5) != '$') { - return FALSE; /* non-local label encountered */ + break; /* non-local label encountered */ } } } - if (!pl) + if (!found) { + for (pl = currPl; pl; pl = pl->next) { + if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel) { + if (strncmp(pl->line, label, len) == 0) { + found = TRUE; + break; /* Found Label */ + } + if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) || + !ISCHARDIGIT(*(pl->line + 1)) || !ISCHARDIGIT(*(pl->line + 2)) || + !ISCHARDIGIT(*(pl->line + 3)) || !ISCHARDIGIT(*(pl->line + 4)) || + *(pl->line + 5) != '$') { + return FALSE; /* non-local label encountered */ + } + } + } + } + + if (!pl || !found) return FALSE; /* did not find the label */ pl = pl->next; while (pl && (pl->isDebug || pl->isComment)) @@ -475,7 +492,6 @@ FBYNAME(labelRefCount) { " in peephole labelRefCount rule.\n", varNumber); } - } else { fprintf(stderr, "*** internal error: labelRefCount peephole restriction" @@ -547,6 +563,68 @@ FBYNAME(labelRefCountChange) { return rc; } +/* newLabel creates new dollar-label and returns it in the specified container. + * Optional second operand may specify initial reference count, by default 1. + * return TRUE if no errors detected + */ +FBYNAME(newLabel) { + int varNumber; + unsigned refCount; + switch (sscanf(cmdLine, " %%%d %u", &varNumber, &refCount)) { + case 1: + refCount = 1; + break; + case 2: + break; + default: + fprintf(stderr, + "*** internal error: newLabel peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + if (varNumber <= 0) { + fprintf(stderr, + "*** internal error: invalid container %%%d" + " in peephole %s rule.\n", + varNumber, __func__); + return FALSE; + } + + if (labelHash == NULL) + buildLabelRefCountHash(head); + + labelHashEntry *entry; + int key; + unsigned maxLabel = 100; // do not use labels below than 00100$ + for (entry = hTabFirstItem(labelHash, &key); entry; + entry = hTabNextItem(labelHash, &key)) { + const char *name = entry->name; + wassert(name); + if (!ISCHARDIGIT(name[0])) + continue; + if (name[strlen(name) - 1] != '$') + continue; + unsigned n; + if (sscanf(name, "%u$", &n) != 1) + continue; + if (maxLabel < n) + maxLabel = n; + } + ++maxLabel; + entry = traceAlloc(&_G.labels, Safe_alloc(sizeof(*entry))); + int len = snprintf(entry->name, SDCC_NAME_MAX, "%05u$", maxLabel); + entry->name[len] = 0; + entry->refCount = refCount; + hTabAddItem(&labelHash, hashSymbolName(entry->name), entry); + + char *value = traceAlloc(&_G.values, Safe_strdup(entry->name)); + hTabAddItem(&vars, varNumber, value); + + return TRUE; +} + /* Within the context of the lines currPl through endPl, determine ** if the variable var contains a symbol that is volatile. Returns ** TRUE only if it is certain that this was not volatile (the symbol @@ -554,27 +632,36 @@ FBYNAME(labelRefCountChange) { ** Returns FALSE if the symbol was found and volatile, the symbol was ** not found, or var was a indirect/pointer addressing mode. */ -static bool notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl) { +static bool notVolatileVariable(const char *var, lineNode *currPl, + lineNode *endPl) { char symname[SDCC_NAME_MAX + 1]; char *p = symname; - char *vp = var; + const char *vp = var; lineNode *cl; operand *op; iCode *last_ic; + const bool global_not_volatile = + currFunc ? !currFunc->funcUsesVolatile : false; + /* Can't tell if indirect accesses are volatile or not, so - ** assume they are, just to be safe. + ** assume they are (if there is a volatile access in the function at all), + *just to be safe. */ + if (var[0] == '#') + return true; + if (var[0] == '(') + return global_not_volatile; if (strstr(var, "(bc)")) - return FALSE; + return global_not_volatile; if (strstr(var, "(de)")) - return FALSE; + return global_not_volatile; if (strstr(var, "(hl)")) - return FALSE; + return global_not_volatile; if (strstr(var, "(ix")) - return FALSE; + return global_not_volatile; if (strstr(var, "(iy")) - return FALSE; + return global_not_volatile; /* Extract a symbol name from the variable */ while (*vp && (*vp != '_')) @@ -587,7 +674,7 @@ static bool notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl) { /* Nothing resembling a symbol name was found, so it can't be volatile */ - return TRUE; + return true; } last_ic = NULL; @@ -632,8 +719,9 @@ static bool notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl) { } } - /* Couldn't find the symbol for some reason. Assume volatile. */ - return FALSE; + /* Couldn't find the symbol for some reason. Assume volatile if the current + * function touches anything volatile. */ + return global_not_volatile; } /* notVolatile: @@ -653,7 +741,6 @@ static bool notVolatileVariable(char *var, lineNode *currPl, lineNode *endPl) { FBYNAME(notVolatile) { int varNumber; char *var; - bool notvol; char *digitend; lineNode *cl; operand *op; @@ -704,9 +791,8 @@ FBYNAME(notVolatile) { var = hTabItemWithKey(vars, varNumber); if (var) { - notvol = notVolatileVariable(var, currPl, endPl); - if (!notvol) - return FALSE; + if (!notVolatileVariable(var, currPl, endPl)) + return false; } else { fprintf(stderr, "*** internal error: var %d not bound" @@ -795,35 +881,37 @@ static const char *operandBaseName(const char *op) { return "iy"; if (!strcmp(op, "ixh") || !strcmp(op, "ixl") || strstr(op, "ix")) return "ix"; + if (!strcmp(op, "a")) + return "af"; return op; } /*-----------------------------------------------------------------*/ -/* notUsed - Check, if value in register is not read again */ +/* notUsed - Check, if values in all registers are not read again */ /*-----------------------------------------------------------------*/ FBYNAME(notUsed) { const char *what; bool ret; + if (!port->peep.notUsed) { + fprintf(stderr, "Function notUsed not initialized in port structure\n"); + return FALSE; + } + set *operands = setFromConditionArgs(cmdLine, vars); - if (!operands || elementsInSet(operands) != 1) { + if (!operands) { fprintf(stderr, "*** internal error: notUsed peephole restriction" - " malformed: %s\n", + " requires operand(s): %s\n", cmdLine); return FALSE; } what = setFirstItem(operands); - - if (!port->peep.notUsed) { - fprintf(stderr, "Function notUsed not initialized in port structure\n"); - return FALSE; - } - - ret = port->peep.notUsed(what, endPl, head); + for (ret = TRUE; ret && what != NULL; what = setNextItem(operands)) + ret = port->peep.notUsed(what, endPl, head); deleteSet(&operands); @@ -831,7 +919,7 @@ FBYNAME(notUsed) { } /*-----------------------------------------------------------------*/ -/* notUsed - Check, if value in register is not read again */ +/* notUsedFrom - Check, if value in register is not read again */ /* starting from label */ /*-----------------------------------------------------------------*/ FBYNAME(notUsedFrom) { @@ -856,6 +944,47 @@ FBYNAME(notUsedFrom) { return FALSE; } +/*-----------------------------------------------------------------*/ +/* unusedReg - find first unused register from specified list and */ +/* assign to container specified as first argument. Fails if all */ +/* of specified registers are accessed for reading. */ +/*-----------------------------------------------------------------*/ +FBYNAME(unusedReg) { + int dst; + int n; + if (sscanf(cmdLine, " %%%d%n", &dst, &n) != 1 || dst <= 0) { + fprintf(stderr, + "*** internal error: unusedReg() peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + set *operands = setFromConditionArgs(&cmdLine[n], vars); + if (!operands || elementsInSet(operands) < 2 || elementsInSet(operands) > 3) { + fprintf(stderr, + "*** internal error: canAssign peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + char *what = setFirstItem(operands); + for (; what != NULL; what = setNextItem(operands)) + if (port->peep.notUsed(what, endPl, head)) + break; + + bool ret = (what != NULL); + if (ret) { + char *s[] = {what, NULL}; + bindVar(dst, s, &vars); + } + + deleteSet(&operands); + + return ret; +} + /*-----------------------------------------------------------------*/ /* canAssign - Check, if we can do ld dst, src. */ /*-----------------------------------------------------------------*/ @@ -895,6 +1024,201 @@ FBYNAME(canAssign) { return FALSE; } +/*-----------------------------------------------------------------*/ +/* canJoinRegs - joins set of registers to combined one, returns */ +/* true, if result register is valid. First operand can be */ +/* 'unordered' if order of registers is not sufficient. Last */ +/* operand should be wildcard. If result is not required, then */ +/* wildcard should be %0. If some of source registers is not */ +/* sufficient then empty string can be passed. */ +/*-----------------------------------------------------------------*/ +FBYNAME(canJoinRegs) { + // Must be specified at least 3 parameters: reg_hi reg_lo and dst + // If destination is not required, then %0 should be specified + if (!port->peep.canJoinRegs) { + fprintf(stderr, "Function canJoinRegs not supported by the port\n"); + return FALSE; + } + + int dstKey; + int i; + for (i = strlen(cmdLine) - 1; i >= 0 && ISCHARSPACE(cmdLine[i]); --i) + ; + for (; i >= 0 && !ISCHARSPACE(cmdLine[i]); --i) + ; + if (i < 0 || cmdLine[i + 1] != '%' || + (cmdLine[i + 1] && + (sscanf(&cmdLine[i + 2], "%d", &dstKey) != 1 || dstKey < 0))) { + fprintf(stderr, + "*** internal error: canJoinRegs peephole restriction" + " has bad result container: %s\n", + &cmdLine[i + 1]); + return FALSE; + } + // parse cmd line without last operand + cmdLine[i] = '\0'; + set *operands = setFromConditionArgs(cmdLine, vars); + cmdLine[i] = ' '; + + if (operands == NULL) { + fprintf(stderr, + "*** internal error: canJoinRegs peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + bool unordered = false; + const char *first = setFirstItem(operands); + if (first && !strcmp(first, "unordered")) { + unordered = true; + deleteSetItem(&operands, (void *)first); + } + + int size = elementsInSet(operands); + if (size < 2) { + fprintf(stderr, + "*** internal error: canJoinRegs peephole restriction" + " requires at least 3 operands: %s\n", + cmdLine); + return FALSE; + } + + const char **regs = (const char **)Safe_alloc((size + 1) * sizeof(*regs)); + i = size; + regs[size] = NULL; /* end of registers */ + // fill regs reversing order (operands have reversed order) + for (set *it = operands; it; it = it->next) + regs[--i] = (const char *)it->item; + + // if unordered specified, then sort elements by ascending order + if (unordered) + qsort(regs, size, sizeof(*regs), + (int (*)(const void *, const void *)) & strcmp); + + char dst[20]; + bool result; + for (;;) { + result = port->peep.canJoinRegs(regs, dst); + if (result || !unordered) + break; + + // do next registers permutation + int i; + // find last regs[i] < regs[i+1] + for (i = size - 2; i >= 0; --i) + if (strcmp(regs[i + 1], regs[i]) > 0) + break; + if (i < 0) + break; /* was last permutation */ + + int j; + // find last regs[j] > regs[i], where j > i + for (j = size - 1; j > i; --j) + if (strcmp(regs[j], regs[i]) > 0) + break; + + // swap regs[j] and regs[i] + const char *t = regs[i]; + regs[i] = regs[j]; + regs[j] = t; + // reverse order from j+1 to end + for (j = j + 1, i = size - 1; j < i; ++j, --i) { + t = regs[j]; + regs[j] = regs[i]; + regs[i] = t; + } + } + + Safe_free(regs); + + if (result && dstKey > 0) { + char *s[] = {dst, NULL}; + bindVar(dstKey, s, &vars); + } + + deleteSet(&operands); + return result; +} + +/*-----------------------------------------------------------------*/ +/* canSplitReg - returns true, if register can be splitted. First */ +/* operand contains complex register name and is required. Other */ +/* operands should be wildcards. If result is not sufficient then */ +/* they can be omited. */ +/*-----------------------------------------------------------------*/ +FBYNAME(canSplitReg) { + if (!port->peep.canSplitReg) { + fprintf(stderr, "Function canSplitReg not supported by the port\n"); + return FALSE; + } + + int i; + // find start of first operand + for (i = 0; cmdLine[i] && ISCHARSPACE(cmdLine[i]); ++i) + ; + if (cmdLine[i] == '\0') { + fprintf(stderr, + "*** internal error: canSplitReg peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + // find end of first operand + for (; cmdLine[i] && !ISCHARSPACE(cmdLine[i]); ++i) + ; + + // parse first operand + char t = cmdLine[i]; + cmdLine[i] = '\0'; + set *operands = setFromConditionArgs(cmdLine, vars); + cmdLine[i] = t; + if (cmdLine[i] == '\0') { + fprintf(stderr, + "*** internal error: canSplitReg peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + // scan remaining operands + int size = 2; + int *varIds = (int *)Safe_alloc(size * sizeof(*varIds)); + const char *cl = &cmdLine[i + 1]; + for (i = 0;; ++i) { + if (i >= size) { + size *= 2; + varIds = (int *)Safe_realloc(varIds, size * sizeof(*varIds)); + } + int len; + if (sscanf(cl, " %%%d%n", &varIds[i], &len) != 1) + break; + if (varIds[i] < 0) { + fprintf(stderr, + "*** internal error: canSplitReg peephole restriction" + " has invalid destination container: %s\n", + cmdLine); + return FALSE; + } + cl += len; + } + size = i; + char(*dst)[16]; + dst = Safe_alloc(size * sizeof(*dst)); + bool ret = port->peep.canSplitReg((char *)setFirstItem(operands), dst, size); + for (i = 0; ret && i < size; ++i) { + if (varIds[i] <= 0) + continue; + char *s[] = {dst[i], NULL}; + bindVar(varIds[i], s, &vars); + } + Safe_free(dst); + Safe_free(varIds); + deleteSet(&operands); + return ret; +} + /*-----------------------------------------------------------------*/ /* operandsNotRelated - returns true if the condition's operands */ /* are not related (taking into account register name aliases). */ @@ -931,6 +1255,48 @@ FBYNAME(operandsNotRelated) { return TRUE; } +/*-----------------------------------------------------------------*/ +/* notSimilar - Check, if one is another's substring */ +/*-----------------------------------------------------------------*/ +FBYNAME(notSimilar) { + set *operands; + const char *op1, *op2; + + operands = setFromConditionArgs(cmdLine, vars); + + if (!operands) { + fprintf(stderr, + "*** internal error: notSimilar peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + while ((op1 = setFirstItem(operands))) { + deleteSetItem(&operands, (void *)op1); + + for (op2 = setFirstItem(operands); op2; op2 = setNextItem(operands)) { + if (strstr(op1, op2) || strstr(op2, op1)) { + deleteSet(&operands); + return FALSE; + } + } + } + + deleteSet(&operands); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* symmParmStack - Caller readjusts stack by the number of bytes + that were pushed in all calls to this function */ +/*-----------------------------------------------------------------*/ +FBYNAME(symmParmStack) { + if (port->peep.symmParmStack) + return port->peep.symmParmStack(); + return FALSE; +} + /*-----------------------------------------------------------------*/ /* notSame - Check, that arguments are pairwise not the same */ /*-----------------------------------------------------------------*/ @@ -963,6 +1329,37 @@ FBYNAME(notSame) { return TRUE; } +/*-----------------------------------------------------------------*/ +/* same - Check if first operand matches any of the remaining */ +/*-----------------------------------------------------------------*/ +FBYNAME(same) { + set *operands; + const char *match, *op; + + operands = setFromConditionArgs(cmdLine, vars); + + if (!operands) { + fprintf(stderr, + "*** internal error: same peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + operands = reverseSet(operands); + + match = setFirstItem(operands); + for (op = setNextItem(operands); op; op = setNextItem(operands)) { + if (strcmp(match, op) == 0) { + deleteSet(&operands); + return TRUE; + } + } + + deleteSet(&operands); + return FALSE; +} + /*-----------------------------------------------------------------*/ /* operandsLiteral - returns true if the condition's operands are */ /* literals. */ @@ -1038,8 +1435,14 @@ FBYNAME(immdInRange) { char r[64], operator[8]; const char *op; long i, j, k, h, low, high, left_l, right_l, order; - const char *padd[] = {"+", "'+'", "\"+\"", "add", "'add'", "\"add\""}; - const char *psub[] = {"-", "'-'", "\"-\"", "sub", "'sub'", "\"sub\""}; + const char *padd[] = {"+", "'+'", "\"+\""}; + const char *psub[] = {"-", "'-'", "\"-\""}; + const char *pmul[] = {"*", "'*'", "\"*\""}; + const char *pdiv[] = {"/", "'/'", "\"/\""}; + const char *pmod[] = {"%", "'%'", "\"%\""}; + const char *pbitand[] = {"&", "'&'", "\"&\""}; + const char *pxor[] = {"^", "'^'", "\"^\""}; + const char *pbitor[] = {"|", "'|'", "\"|\""}; for (i = order = 0; order < 6;) { // pick up one parameter in the temp buffer r[64] @@ -1049,6 +1452,8 @@ FBYNAME(immdInRange) { ; if (!cmdLine[i]) // unexpected end return immdError("no enough input", "", cmdLine); + else if (j >= 64) + return immdError("buffer overflow", "", cmdLine); else { for (k = i; k < j; k++) r[k - i] = cmdLine[k]; @@ -1076,7 +1481,7 @@ FBYNAME(immdInRange) { if (!immdGet(r + 1, &k) || !(op = hTabItemWithKey(vars, (int)k))) return immdError("bad left operand", r, cmdLine); else if (!immdGet(op, &left_l)) - return immdError("bad left operand", op, r); + return FALSE; } else return immdError("bad left operand", r, cmdLine); break; @@ -1093,7 +1498,8 @@ FBYNAME(immdInRange) { return immdError("bad right operand", r, cmdLine); break; case 5: // result - if (r[0] != '%' || !immdGet(r + 1, &h)) + if (r[0] != '%' || + !(immdGet(r + 1, &h) || (r[1] == 'x' && immdGet(r + 2, &h)))) return immdError("bad result container", r, cmdLine); break; default: // should not reach @@ -1118,13 +1524,67 @@ FBYNAME(immdInRange) { j = 1; break; } + if (!j) + for (k = 0; k < sizeof(pmul) / sizeof(pmul[0]); k++) // mul + if (strcmp(operator, pmul[k]) == 0) { + i = left_l * right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof(pdiv) / sizeof(pdiv[0]); k++) // div + if (strcmp(operator, pdiv[k]) == 0) { + if (right_l == 0) + return immdError("division by zero", "", cmdLine); + i = left_l / right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof(pmod) / sizeof(pmod[0]); k++) // mod + if (strcmp(operator, pmod[k]) == 0) { + if (right_l == 0) + return immdError("division by zero", "", cmdLine); + i = left_l % right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof(pbitand) / sizeof(pbitand[0]); k++) // and + if (strcmp(operator, pbitand[k]) == 0) { + i = left_l & right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof(pxor) / sizeof(pxor[0]); k++) // xor + if (strcmp(operator, pxor[k]) == 0) { + i = left_l ^ right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof(pbitor) / sizeof(pbitor[0]); k++) // or + if (strcmp(operator, pbitor[k]) == 0) { + i = left_l | right_l; + j = 1; + break; + } if (!j) return immdError("bad operator", operator, cmdLine); // bind the result if ((low <= i && i <= high) || (high <= i && i <= low)) { + bool hex = false; + if (r[1] == 'x') { + hex = true; + r[1] = '0'; + } char *p[] = {r, NULL}; - sprintf(r, "%ld", i); + if (!hex) + sprintf(r, "%ld", i); + else + sprintf(r, "0x%lx", i); bindVar((int)h, p, &vars); return TRUE; } else { @@ -1132,11 +1592,84 @@ FBYNAME(immdInRange) { } } +/*-----------------------------------------------------------------*/ +/* inSequence - Check that numerical constants are in sequence */ +/*-----------------------------------------------------------------*/ +FBYNAME(inSequence) { + set *operands; + const char *op; + long seq, val, stride; + + if ((operands = setFromConditionArgs(cmdLine, vars)) == NULL) { + fprintf(stderr, + "*** internal error: inSequence peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + operands = reverseSet(operands); + + op = setFirstItem(operands); + if ((immdGet(op, &stride) == NULL) || + ((op = setNextItem(operands)) == NULL)) { + fprintf(stderr, + "*** internal error: inSequence peephole restriction" + " malformed: %s\n", + cmdLine); + return FALSE; + } + + for (seq = LONG_MIN; op; op = setNextItem(operands)) { + if ((immdGet(op, &val) == NULL) || + ((seq != LONG_MIN) && (val != seq + stride))) { + deleteSet(&operands); + return FALSE; + } + seq = val; + } + + deleteSet(&operands); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* isPort - return true if port name matches one of args */ +/*-----------------------------------------------------------------*/ +FBYNAME(isPort) { + const char *name; + bool ret = false; + + set *operands = setFromConditionArgs(cmdLine, vars); + + if (!operands) { + fprintf(stderr, + "*** internal error: isPort peephole restriction" + " malformed: %s\n", + cmdLine); + return false; + } + + while (name = setFirstItem(operands)) { + deleteSetItem(&operands, (void *)name); + + if (strcmp(port->target, name) == 0) { + ret = true; + break; + } + } + + deleteSet(&operands); + + return ret; +} + static const struct ftab { char *fname; int (*func)(hTab *, lineNode *, lineNode *, lineNode *, char *); } ftab[] = // sorted on the number of times used - { // in the peephole rules on 2010-06-12 + { + // in the peephole rules on 2010-06-12 { "labelRefCount", labelRefCount // 161 }, @@ -1158,12 +1691,21 @@ static const struct ftab { { "operandsNotRelated", operandsNotRelated // 28 }, + { + "same", same // z88dk z80 + }, { "labelJTInRange", labelJTInRange // 13 }, + { + "24bitMode", flat24bitMode // 9 + }, { "canAssign", canAssign // 8 }, + { + "inSequence", inSequence // z88dk z80 + }, { "optimizeReturn", optimizeReturn // ? just a guess }, @@ -1176,9 +1718,6 @@ static const struct ftab { { "operandsLiteral", operandsLiteral // 6 }, - { - "portIsDS390", portIsDS390 // 5 - }, { "labelIsUncondJump", labelIsUncondJump // 4 }, @@ -1194,7 +1733,15 @@ static const struct ftab { { "okToRemoveSLOC", okToRemoveSLOC // 0 }, - {"immdInRange", immdInRange}}; + {"immdInRange", immdInRange}, + {"notSimilar", notSimilar}, + {"symmParmStack", symmParmStack}, + {"isPort", isPort}, + {"canJoinRegs", canJoinRegs}, + {"canSplitReg", canSplitReg}, + {"unusedReg", unusedReg}, + {"newLabel", newLabel}, +}; /*-----------------------------------------------------------------*/ /* callFuncByName - calls a function as defined in the table */ @@ -1302,7 +1849,7 @@ static peepRule *newPeepRule(lineNode *match, lineNode *replace, char *cond, } else pr->cond = NULL; - pr->vars = newHashTable(100); + pr->vars = newHashTable(16); /* if root is empty */ if (!rootRules) @@ -1345,13 +1892,13 @@ static peepRule *newPeepRule(lineNode *match, lineNode *replace, char *cond, /*-----------------------------------------------------------------*/ /* getPeepLine - parses the peep lines */ /*-----------------------------------------------------------------*/ -static void getPeepLine(lineNode **head, char **bpp) { +static void getPeepLine(lineNode **head, const char **bpp) { char lines[MAX_PATTERN_LEN]; char *lp; int isComment; lineNode *currL = NULL; - char *bp = *bpp; + const char *bp = *bpp; while (1) { if (!*bp) { @@ -1400,11 +1947,12 @@ static void getPeepLine(lineNode **head, char **bpp) { /*-----------------------------------------------------------------*/ /* readRules - reads the rules from a string buffer */ /*-----------------------------------------------------------------*/ -static void readRules(char *bp) { +static void readRules(const char *bp) { char restart = 0, barrier = 0; char lines[MAX_PATTERN_LEN]; size_t safetycounter; - char *lp, *rp; + char *lp; + const char *rp; lineNode *match; lineNode *replace; lineNode *currL = NULL; @@ -1469,13 +2017,25 @@ static void readRules(char *bp) { getPeepLine(&replace, &bp); /* look for a 'if' */ - while ((ISCHARSPACE(*bp) || *bp == '\n') && *bp) - bp++; + while ( + (ISCHARSPACE(*bp) || *bp == '\n' || (*bp == '/' && *(bp + 1) == '/')) && + *bp) { + ++bp; + if (*bp == '/') + while (*bp && *bp != '\n') + ++bp; + } if (strncmp(bp, "if", 2) == 0) { bp += 2; - while ((ISCHARSPACE(*bp) || *bp == '\n') && *bp) + while ( + (ISCHARSPACE(*bp) || *bp == '\n' || (*bp == '/' && *(bp + 1) == '/')) && + *bp) { bp++; + if (*bp == '/') + while (*bp && *bp != '\n') + bp++; + } if (!*bp) { fprintf(stderr, "expected condition name\n"); return; @@ -1522,7 +2082,7 @@ static void readRules(char *bp) { /*-----------------------------------------------------------------*/ /* keyForVar - returns the numeric key for a var */ /*-----------------------------------------------------------------*/ -static int keyForVar(char *d) { +static int keyForVar(const char *d) { int i = 0; while (ISCHARDIGIT(*d)) { @@ -1573,10 +2133,16 @@ static void bindVar(int key, char **s, hTab **vtab) { /*-----------------------------------------------------------------*/ /* matchLine - matches one line */ /*-----------------------------------------------------------------*/ -static bool matchLine(char *s, char *d, hTab **vars) { +static bool matchLine(char *s, const char *d, hTab **vars) { if (!s || !(*s)) return FALSE; + /* skip leading white spaces */ + while (ISCHARSPACE(*s)) + s++; + while (ISCHARSPACE(*d)) + d++; + while (*s && *d) { /* skip white space in both */ while (ISCHARSPACE(*s)) @@ -1586,7 +2152,7 @@ static bool matchLine(char *s, char *d, hTab **vars) { /* if the destination is a var */ if (*d == '%' && ISCHARDIGIT(*(d + 1)) && vars) { - char *v = hTabItemWithKey(*vars, keyForVar(d + 1)); + const char *v = hTabItemWithKey(*vars, keyForVar(d + 1)); /* if the variable is already bound then it MUST match with dest */ if (v) { @@ -1601,22 +2167,31 @@ static bool matchLine(char *s, char *d, hTab **vars) { d++; while (ISCHARDIGIT(*d)) d++; - + } else if (ISCHARSPACE(*s) && + ISCHARSPACE(*d)) /* whitespace sequences match any whitespace + sequences */ + { while (ISCHARSPACE(*s)) s++; while (ISCHARSPACE(*d)) d++; - } - - /* they should be an exact match other wise */ - if (*s && *d) { + } else if (*s == ',' && + *d == + ',') /* Allow comman to match comma followed by whitespace */ + { + s++, d++; + while (ISCHARSPACE(*s)) + s++; + while (ISCHARSPACE(*d)) + d++; + } else if (*s && *d) /* they should be an exact match otherwise */ + { if (*s++ != *d++) return FALSE; } } - /* get rid of the trailing spaces - in both source & destination */ + /* skip trailing whitespaces */ if (*s) while (ISCHARSPACE(*s)) s++; @@ -1669,6 +2244,11 @@ static bool matchRule(lineNode *pl, lineNode **mtail, peepRule *pr, if (!rpl) { /* if this rule has additional conditions */ if (pr->cond) { + /* constraints which uses variables as destination container + requires to vars table to be defined */ + if (!pr->vars) + pr->vars = newHashTable(128); + if (callFuncByName(pr->cond, pr->vars, pl, spl, head)) { *mtail = spl; return TRUE; @@ -1984,6 +2564,7 @@ bool isLabelDefinition(const char *line, const char **start, int *len, #2970351. */ bool isLabelReference(const char *line, const char **start, int *len) { const char *s, *e; + s = line; while (ISCHARSPACE(*s)) ++s; @@ -2137,6 +2718,12 @@ void peepHole(lineNode **pls) { lineNode *mtail = NULL; bool restart, replaced; +#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16 + /* The PIC port uses a different peep hole optimizer based on "pCode" */ + if (TARGET_PIC_LIKE) + return; +#endif + assert(labelHash == NULL); do { @@ -2265,6 +2852,24 @@ void initPeepHole(void) { /* override nopeep setting, default rules have not been read */ options.nopeep = 0; } + +#if !OPT_DISABLE_PIC14 + /* Convert the peep rules into pcode. + NOTE: this is only support in the PIC port (at the moment) + */ + if (TARGET_IS_PIC14) + peepRules2pCode(rootRules); +#endif + +#if !OPT_DISABLE_PIC16 + /* Convert the peep rules into pcode. + NOTE: this is only support in the PIC port (at the moment) + and the PIC16 port (VR 030601) + */ + if (TARGET_IS_PIC16) + pic16_peepRules2pCode(rootRules); + +#endif } /*-----------------------------------------------------------------*/ diff --git a/src/SDCCptropt.c b/src/SDCCptropt.c index 32a5a93ca..7e3a669d0 100644 --- a/src/SDCCptropt.c +++ b/src/SDCCptropt.c @@ -50,7 +50,7 @@ static iCode *findPointerGetSet(iCode *sic, operand *op) { return NULL; } -static int pattern1(iCode *sic) { +static int pattern1(iCode *sic, eBBlock *ebb) { /* this is what we do. look for sequences like iTempX := _SOME_POINTER_; @@ -97,12 +97,14 @@ static int pattern1(iCode *sic) { /* and put them after the pointer get/set icode */ if ((st->next = pgs->next)) st->next->prev = st; + if (!st->next) + ebb->ech = st; pgs->next = sh; sh->prev = pgs; return 1; } -static int pattern2(iCode *sic) { +static int pattern2(iCode *sic, eBBlock *ebb) { /* this is what we do. look for sequences like iTempX := _SOME_POINTER_; @@ -161,6 +163,8 @@ static int pattern2(iCode *sic) { /* and put them after the pointer get/set icode */ if ((st->next = pgs->next)) st->next->prev = st; + if (!st->next) + ebb->ech = st; pgs->next = sh; sh->prev = pgs; return 1; @@ -170,10 +174,10 @@ static int pattern2(iCode *sic) { /* ptrPostIncDecOpts - will do some pointer post increment optimizations */ /* this will help register allocation amongst others */ /*-----------------------------------------------------------------------*/ -void ptrPostIncDecOpt(iCode *sic) { - if (pattern1(sic)) +void ptrPostIncDecOpt(iCode *sic, eBBlock *ebb) { + if (pattern1(sic, ebb)) return; - pattern2(sic); + pattern2(sic, ebb); } /*-----------------------------------------------------------------------*/ diff --git a/src/SDCCptropt.h b/src/SDCCptropt.h index d69e53702..abb1e3545 100644 --- a/src/SDCCptropt.h +++ b/src/SDCCptropt.h @@ -26,7 +26,7 @@ #ifndef SDCCPTROPT_H #define SDCCPTROPT_H 1 -void ptrPostIncDecOpt(iCode *); +void ptrPostIncDecOpt(iCode *, eBBlock *); int ptrAddition(iCode *); symbol *ptrBaseRematSym(symbol *); int ptrPseudoSymSafe(symbol *, iCode *); diff --git a/src/SDCCralloc.hpp b/src/SDCCralloc.hpp index b0f9105fa..047f3ecea 100644 --- a/src/SDCCralloc.hpp +++ b/src/SDCCralloc.hpp @@ -53,25 +53,20 @@ #include #include +// Workaround for boost bug #11880 #include -#if BOOST_VERSION >= 106000 -// Workaround for https://svn.boost.org/trac/boost/ticket/11880 +#if BOOST_VERSION == 106000 #include #endif + +#include +#include #include #include #include -#include "SDCCtree_dec.hpp" #include "common.h" -#ifdef HAVE_STX_BTREE_SET_H -#include -#endif -#ifdef HAVE_STX_BTREE_MAP_H -#include -#endif - extern "C" { #include "SDCCbtree.h" } @@ -126,21 +121,16 @@ struct i_assignment_t { } }; -#ifdef HAVE_STX_BTREE_SET_H -typedef stx::btree_set varset_t; // Faster than std::set -#else -typedef std::set varset_t; -#endif -// typedef std::set, boost::fast_pool_allocator > -// varset_t; // Slower than ordinary std::set +typedef std::vector + varset_t; // Faster than std::set, std::tr1::unordered_set and + // stx::btree_set here. -#ifdef HAVE_STX_BTREE_MAP_H -typedef stx::btree_map icosts_t; // Faster than std::map -#else -typedef std::map icosts_t; -#endif -// typedef std::tr1::unordered_set varset_t; // Speed about the same as -// std::set +typedef boost::container::flat_map + icosts_t; // Faster than std::map and stx::btree_map here. + +typedef std::vector cfg_alive_t; // Faster than stx::btree_set here . +typedef boost::container::flat_set + cfg_dying_t; // Faster than stx::btree_set and std::set here. struct assignment { float s; @@ -162,6 +152,8 @@ struct assignment { ai_end = a.local.end(); for (i = local.begin(), ai = a.local.begin();; ++i, ++ai) { + if (i == i_end && ai == ai_end) + return (false); if (i == i_end) return (true); if (ai == ai_end) @@ -194,15 +186,17 @@ struct tree_dec_node { // weight should be processed first. }; -typedef std::multimap operand_map_t; -// typedef stx::btree_multimap operand_map_t; // Slightly slower -// than std::multimap. +typedef boost::container::flat_multimap + operand_map_t; // Faster than std::multimap and + // stx::btree_multimap here. struct cfg_node { iCode *ic; operand_map_t operands; - std::set alive; - std::set dying; + cfg_alive_t alive; + cfg_dying_t dying; + + std::set stack_alive; #ifdef DEBUG_SEGV cfg_node(void); @@ -235,6 +229,13 @@ typedef boost::adjacency_list cfg_sym_t; +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif + +#include "SDCCtree_dec.hpp" + // Cost function. Port-specific. template static float instruction_cost(const assignment &a, unsigned short int i, @@ -276,14 +277,90 @@ add_operand_to_cfg_node(cfg_node &n, operand *o, sym_to_index.end()) { if (n.operands.find(OP_SYMBOL_CONST(o)->key) == n.operands.end()) for (k = 0; k < OP_SYMBOL_CONST(o)->nRegs; k++) - n.operands.insert(std::pair( + n.operands.insert(std::pair( OP_SYMBOL_CONST(o)->key, sym_to_index[std::pair(OP_SYMBOL_CONST(o)->key, k)])); } } +// Check if the live-range of variable i is connected +#if 0 +// This check was too expensive - Profiling shows that compiling the Dhrystone benchmark for stm8 with default options, we spent about a quarter of compiler runtime in here! +// Profiling shows that we spent a significant amount of time on the first call to copy_graph() +// Todo: Improve efficiency, e.g. using subgraph or filtered_graph to avoid the costly first call to copy_graph() +// Issues to solve: cfg2 is undirected, cfg is bidirectional; this makes use of subgraph or filtered_graph harder. +static bool liverange_connected(cfg_t &cfg, var_t i) +{ + cfg_sym_t cfg2; + boost::copy_graph(cfg, cfg2, boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); // This call to copy_graph is expensive! + for (int j = boost::num_vertices(cfg) - 1; j >= 0; j--) + { + if (std::find(cfg[j].alive.begin(), cfg[j].alive.end(), i) == cfg[j].alive.end()) + { + boost::clear_vertex(j, cfg2); + boost::remove_vertex(j, cfg2); + } + } + + std::vector::vertices_size_type> component(num_vertices(cfg2)); + + return(boost::connected_components(cfg2, &component[0]) <= 1); +} +#else +// A not very elegant, but faster check +static inline int component_size_impl(const cfg_t &cfg, + const std::vector &life, var_t v, + int i, std::vector &visited) { + typename boost::graph_traits::in_edge_iterator in, in_end; + typename boost::graph_traits::out_edge_iterator out, out_end; + + int size = 1; + visited[i] = true; + + for (boost::tie(in, in_end) = boost::in_edges(i, cfg); in != in_end; ++in) + if (life[boost::source(*in, cfg)] && !visited[boost::source(*in, cfg)]) + size += + component_size_impl(cfg, life, v, boost::source(*in, cfg), visited); + + for (boost::tie(out, out_end) = boost::out_edges(i, cfg); out != out_end; + ++out) + if (life[boost::target(*out, cfg)] && !visited[boost::target(*out, cfg)]) + size += + component_size_impl(cfg, life, v, boost::target(*out, cfg), visited); + + return (size); +} + +static inline int component_size(const cfg_t &cfg, + const std::vector &life, var_t v, + int i) { + std::vector visited(boost::num_vertices(cfg)); + + return (component_size_impl(cfg, life, v, i, visited)); +} + +static bool liverange_connected(const cfg_t &cfg, var_t v) { + std::vector life(boost::num_vertices(cfg)); + int num_life = 0; + int last_life; + + for (int i = 0; i < boost::num_vertices(cfg); i++) + if (std::find(cfg[i].alive.begin(), cfg[i].alive.end(), v) != + cfg[i].alive.end()) { + life[i] = true; + num_life++; + last_life = i; + } + + if (!num_life) + return (true); + + return (component_size(cfg, life, v, last_life) >= num_life); +} +#endif + // A quick-and-dirty function to get the CFG from sdcc. -static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { +static iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { eBBlock **ebbs = ebbi->bbOrder; iCode *start_ic; iCode *ic; @@ -291,12 +368,25 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { std::map key_to_index; std::map, var_t> sym_to_index; + if (currFunc) + currFunc->funcDivFlagSafe = 1; + start_ic = iCodeLabelOptimize(iCodeFromeBBlock(ebbs, ebbi->count)); { int i; var_t j; wassertl(!boost::num_vertices(cfg), "CFG non-empty before creation."); for (ic = start_ic, i = 0, j = 0; ic; ic = ic->next, i++) { + if (currFunc) + currFunc->funcDivFlagSafe &= + !(ic->op == INLINEASM || ic->op == '/' || ic->op == '%' || + ic->op == PCALL || + ic->op == CALL && (IS_OP_LITERAL(IC_LEFT(ic)) || + !OP_SYMBOL(IC_LEFT(ic))->funcDivFlagSafe) || + ic->op == RIGHT_OP && + IS_OP_LITERAL(IC_RIGHT( + ic))); // Right shift might be implemented using division. + #ifdef DEBUG_SEGV default_constructor_of_cfg_node_called = false; #endif @@ -320,6 +410,8 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { extra_ic_generated(ic); cfg[i].ic = ic; + ic->rSurv = newBitVect(port->num_regs); // Never freed. Memory leak? + ic->rMask = newBitVect(port->num_regs); // Never freed. Memory leak? if (ic->generated) continue; @@ -414,16 +506,18 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { sym_to_index.end()); wassertl(key_to_index[ic->key] < boost::num_vertices(cfg), "Node not in CFG."); - cfg[key_to_index[ic->key]].alive.insert( + cfg[key_to_index[ic->key]].alive.push_back( sym_to_index[std::pair(i, k)]); } // TODO: Move this to a place where it also works when using the old // allocator! - if (isym->block) - isym->block = btree_lowest_common_ancestor(isym->block, ic->block); - else - isym->block = ic->block; + isym->block = btree_lowest_common_ancestor(isym->block, ic->block); + // If this symbol has a spill location, ensure the spill location is + // also allocated in a compatible block + if (SYM_SPIL_LOC(isym)) + SYM_SPIL_LOC(isym)->block = btree_lowest_common_ancestor( + SYM_SPIL_LOC(isym)->block, isym->block); } } @@ -465,24 +559,9 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { } #endif - // Check for unconnected live ranges, some might have survived dead code - // elimination. This is essentially a workaround for broken dead code - // alimination. Todo: Improve efficiency, e.g. using subgraph or - // filtered_graph. Todo: Split live ranges instead? - for (var_t i = boost::num_vertices(con) - 1; i >= 0; i--) { - cfg_sym_t cfg2; - boost::copy_graph( - cfg, cfg2, - boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); - for (int j = boost::num_vertices(cfg) - 1; j >= 0; j--) { - if (cfg[j].alive.find(i) == cfg[j].alive.end()) { - boost::clear_vertex(j, cfg2); - boost::remove_vertex(j, cfg2); - } - } - std::vector::vertices_size_type> component( - num_vertices(cfg2)); - if (boost::connected_components(cfg2, &component[0]) > 1) { + // Check for unconnected live ranges, some might have survived earlier stages. + for (var_t i = (var_t)boost::num_vertices(con) - 1; i >= 0; i--) + if (!liverange_connected(cfg, i)) { // Non-connected CFGs are created by at least GCSE and lospre. We now have // a live-range splitter that fixes them, so this should no longer be // necessary, but we leave this code here for now, so in case one gets @@ -503,24 +582,29 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { for (boost::graph_traits::vertices_size_type j = 0; j < boost::num_vertices(cfg) - 1; j++) { - if (cfg[j].alive.find(i) != cfg[j].alive.end()) { - for (boost::graph_traits::vertices_size_type k = 0; - k < boost::num_vertices(cfg) - 1; k++) { - if (component[j] == component[k]) - cfg[k].alive.insert(i); - } - } + if (std::find(cfg[j].alive.begin(), cfg[j].alive.end(), i) == + cfg[j].alive.end()) + continue; + + for (boost::graph_traits::vertices_size_type k = 0; + k < boost::num_vertices(cfg) - 1; k++) + if (component[j] == component[k] && + std::find(cfg[k].alive.begin(), cfg[k].alive.end(), i) == + cfg[k].alive.end()) + cfg[k].alive.push_back(i); } } - } + // Sort alive and setup dying. for (boost::graph_traits::vertices_size_type i = 0; i < num_vertices(cfg); i++) { - cfg[i].dying = cfg[i].alive; + std::sort(cfg[i].alive.begin(), cfg[i].alive.end()); + cfg[i].dying = cfg_dying_t(cfg[i].alive.begin(), cfg[i].alive.end()); + ; typedef boost::graph_traits::adjacency_iterator adjacency_iter_t; adjacency_iter_t j, j_end; for (boost::tie(j, j_end) = adjacent_vertices(i, cfg); j != j_end; ++j) { - std::set::const_iterator v, v_end; + cfg_alive_t::const_iterator v, v_end; for (v = cfg[*j].alive.begin(), v_end = cfg[*j].alive.end(); v != v_end; ++v) { const symbol *const vsym = @@ -530,7 +614,8 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { const operand *const right = IC_RIGHT(cfg[*j].ic); const operand *const result = IC_RESULT(cfg[*j].ic); - if (!POINTER_SET(cfg[*j].ic) && + if (!(POINTER_SET(cfg[*j].ic) || + cfg[*j].ic->op == SET_VALUE_AT_ADDRESS) && (!left || !IS_SYMOP(left) || OP_SYMBOL_CONST(left)->key != vsym->key) && (!right || !IS_SYMOP(right) || @@ -547,12 +632,12 @@ static inline iCode *create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) { // Construct conflict graph for (boost::graph_traits::vertices_size_type i = 0; i < num_vertices(cfg); i++) { - std::set::const_iterator v, v_end; + cfg_alive_t::const_iterator v, v_end; const iCode *ic = cfg[i].ic; for (v = cfg[i].alive.begin(), v_end = cfg[i].alive.end(); v != v_end; ++v) { - std::set::const_iterator v2, v2_end; + cfg_alive_t::const_iterator v2, v2_end; // Conflict between operands are handled by // add_operand_conflicts_in_node(). @@ -626,8 +711,41 @@ void assignments_introduce_instruction(assignment_list_t &alist, unsigned short int i, const G_t &G) { assignment_list_t::iterator ai, ai_end; +#if !defined(_MSC_VER) // Efficient code - reduces total SDCC runtime by + // about 5.5% vs. code below, but doesn't work with + // MSVC++ (at least up to MSVC++ 2015) + struct inserter_t { + explicit inserter_t(const std::vector &g, i_assignment_t &a) + : global(g), ia(a) {} + inserter_t &operator=(var_t v) { + if (global[v] >= 0) + ia.add_var(v, global[v]); + return (*this); + } + inserter_t &operator*() { return (*this); } + inserter_t &operator++() { return (*this); } + inserter_t &operator++(int i) { + i; + return (*this); + } + + private: + const std::vector &global; + i_assignment_t &ia; + }; + + for (ai = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) { + i_assignment_t ia; + + std::set_intersection(ai->local.begin(), ai->local.end(), + G[i].alive.begin(), G[i].alive.end(), + inserter_t(ai->global, ia)); + + ai->i_assignment = ia; + } +#else // Human-readable code for (ai = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) { - std::set i_variables; + varset_t i_variables; std::set_intersection(ai->local.begin(), ai->local.end(), G[i].alive.begin(), G[i].alive.end(), @@ -635,13 +753,15 @@ void assignments_introduce_instruction(assignment_list_t &alist, i_assignment_t ia; - std::set::const_iterator v, v_end; + varset_t::const_iterator v, v_end; + for (v = i_variables.begin(), v_end = i_variables.end(); v != v_end; ++v) if (ai->global[*v] >= 0) ia.add_var(*v, ai->global[*v]); ai->i_assignment = ia; } +#endif } template @@ -662,7 +782,10 @@ static void assignments_introduce_variable(assignment_list_t &alist, a = *ai; ai->marked = true; a.marked = false; - a.local.insert(v); + varset_t::iterator i = + std::lower_bound(a.local.begin(), a.local.end(), v); + if (i == a.local.end() || *i != v) + a.local.insert(i, v); } a.global[v] = r; a.i_assignment.add_var(v, r); @@ -684,6 +807,9 @@ struct assignment_rep { template float compability_cost(const assignment &a, const assignment &ac, const I_t &I) { + typedef + typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + float c = 0.0f; varset_t::const_iterator vi, vi_end; @@ -736,24 +862,70 @@ static void drop_worst_assignments(assignment_list_t &alist, std::cout.flush(); #endif +#if 0 assignment_rep *arep = new assignment_rep[alist_size]; - for (n = 0, ai = alist.begin(); n < alist_size; ++ai, n++) { - arep[n].i = ai; - arep[n].s = ai->s + rough_cost_estimate(*ai, i, G, I) + - compability_cost(*ai, ac, I); - } + for (n = 0, ai = alist.begin(); n < alist_size; ++ai, n++) + { + arep[n].i = ai; + arep[n].s = ai->s + rough_cost_estimate(*ai, i, G, I) + compability_cost(*ai, ac, I); + } + + std::nth_element(arep + 1, arep + options.max_allocs_per_node / port->num_regs, arep + alist_size); + + //std::cout << "nth elem. est. cost: " << arep[options.max_allocs_per_node / port->num_regs].s << "\n"; std::cout.flush(); + + for (n = options.max_allocs_per_node / port->num_regs + 1; n < alist_size; n++) + alist.erase(arep[n].i); +#else // More efficient, reduces total SDCC runtime by about 1%. + + size_t endsize = options.max_allocs_per_node / port->num_regs + 1; + size_t arep_maxsize = std::min(alist_size, endsize * 2) + 1; + size_t m, k; + float bound = std::numeric_limits::infinity(); + + assignment_rep *arep = new assignment_rep[arep_maxsize]; + + for (m = 0, n = 1, ai = alist.begin(), ++ai; n < alist_size; n++) { + float s = ai->s; + + if (s > bound) { + alist.erase(ai++); + continue; + } + s += compability_cost(*ai, ac, I); + if (s > bound) { + alist.erase(ai++); + continue; + } + s += rough_cost_estimate(*ai, i, G, I); + if (s > bound) { + alist.erase(ai++); + continue; + } + + if (m >= arep_maxsize - 1) { + std::nth_element(arep, arep + (endsize - 1), arep + m); + for (k = endsize; k < m; k++) + alist.erase(arep[k].i); + bound = arep[endsize - 1].s; - std::nth_element(arep + 1, - arep + options.max_allocs_per_node / port->num_regs, - arep + alist_size); + m = endsize; + } + + arep[m].i = ai; + arep[m].s = s; - // std::cout << "nth elem. est. cost: " << arep[options.max_allocs_per_node / - // port->num_regs].s << "\n"; std::cout.flush(); + m++; + + ++ai; + } - for (n = options.max_allocs_per_node / port->num_regs + 1; n < alist_size; - n++) + std::nth_element(arep, arep + (endsize - 1), arep + m); + + for (n = endsize; n < m; n++) alist.erase(arep[n].i); +#endif delete[] arep; } @@ -897,9 +1069,10 @@ tree_dec_ralloc_forget(T_t &T, std::set old_inst; std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); + wassert(old_inst.size() == 1); unsigned short int i = *(old_inst.begin()); - std::set old_vars; + varset_t old_vars; std::set_difference(T[*c].alive.begin(), T[*c].alive.end(), T[t].alive.begin(), T[t].alive.end(), std::inserter(old_vars, old_vars.end())); @@ -907,18 +1080,18 @@ tree_dec_ralloc_forget(T_t &T, assignment_list_t::iterator ai, aif; // Restrict assignments (locally) to current variables. + varset_t newlocal; for (ai = alist.begin(); ai != alist.end(); ++ai) { - // Erasing by iterators doesn't work with B-Trees, and erasing by value - // invalidates iterators. - std::set::const_iterator oi, oi_end; - for (oi = old_vars.begin(), oi_end = old_vars.end(); oi != oi_end; ++oi) - ai->local.erase(*oi); + newlocal.clear(); + std::set_difference(ai->local.begin(), ai->local.end(), old_vars.begin(), + old_vars.end(), + std::inserter(newlocal, newlocal.end())); + std::swap(ai->local, newlocal); ai->i_costs.erase(i); } alist.sort(); - // std::sort(alist.begin(), alist.end()); // Collapse (locally) identical assignments. for (ai = alist.begin(); ai != alist.end();) { @@ -929,11 +1102,8 @@ tree_dec_ralloc_forget(T_t &T, alist.erase(aif); aif = ai; ++ai; - } else { - alist.erase(ai); - ai = aif; - ++ai; - } + } else + ai = alist.erase(ai); } } @@ -981,50 +1151,45 @@ tree_dec_ralloc_join(T_t &T, ++c; c3 = c; - assignment_list_t &alist1 = T[t].assignments; + assignment_list_t &alist = T[t].assignments; assignment_list_t &alist2 = T[*c2].assignments; - assignment_list_t &alist3 = T[*c3].assignments; + std::swap(alist, T[*c3].assignments); + alist.sort(); alist2.sort(); - // std::sort(alist2.begin(), alist2.end()); - alist3.sort(); - // std::sort(alist3.begin(), alist3.end()); - - assignment_list_t::iterator ai2, ai3; - for (ai2 = alist2.begin(), ai3 = alist3.begin(); - ai2 != alist2.end() && ai3 != alist3.end();) { - if (assignments_locally_same(*ai2, *ai3)) { - ai2->s += ai3->s; + + assignment_list_t::iterator ai, ai2; + for (ai = alist.begin(), ai2 = alist2.begin(); + ai != alist.end() && ai2 != alist2.end();) { + if (assignments_locally_same(*ai, *ai2)) { + ai->s += ai2->s; // Avoid double-counting instruction costs. std::set::iterator bi; for (bi = T[t].bag.begin(); bi != T[t].bag.end(); ++bi) - ai2->s -= ai2->i_costs[*bi]; - for (size_t i = 0; i < ai2->global.size(); i++) - ai2->global[i] = - ((ai2->global[i] != -1) ? ai2->global[i] : ai3->global[i]); - alist1.push_back(*ai2); + ai->s -= ai->i_costs[*bi]; + for (size_t i = 0; i < ai->global.size(); i++) + ai->global[i] = + ((ai->global[i] != -1) ? ai->global[i] : ai2->global[i]); + ++ai; ++ai2; - ++ai3; - } else if (*ai2 < *ai3) { + } else if (*ai < *ai2) + ai = alist.erase(ai); + else if (*ai2 < *ai) ++ai2; - continue; - } else if (*ai3 < *ai2) { - ++ai3; - continue; - } } + while (ai != alist.end()) + ai = alist.erase(ai); alist2.clear(); - alist3.clear(); #ifdef DEBUG_RALLOC_DEC - std::cout << "Remaining assignments: " << alist1.size() << "\n"; + std::cout << "Remaining assignments: " << alist.size() << "\n"; std::cout.flush(); #endif #ifdef DEBUG_RALLOC_DEC_ASS - std::list::iterator ai; - for (ai = alist1.begin(); ai != alist1.end(); ++ai) { + for (std::list::iterator ai = alist.begin(); ai != alist.end(); + ++ai) { print_assignment(*ai); std::cout << "\n"; } @@ -1076,11 +1241,11 @@ tree_dec_ralloc_nodes(T_t &T, c0 = *c++; c1 = *c; - if (T[c0].weight < T[c1].weight) // Minimize memory consumption. - { - /*std::swap (c0, c1)*/ /* Causes code size regressions - don't know why. - */ - } + if (T[c0].weight < + T[c1].weight) // Minimize memory consumption needed for keeping + // intermediate results. As a side effect, this also helps + // the ac mechanism in the heuristic. + std::swap(c0, c1); tree_dec_ralloc_nodes(T, c0, G, I, ac, assignment_optimal); { @@ -1196,8 +1361,11 @@ template static void good_re_root(T_t &T) { // Dump conflict graph, with numbered nodes, show live variables at each node. static void dump_con(const con_t &con) { + if (!currFunc) + return; + std::ofstream dump_file( - (std::string(dstFileName) + ".dumpcon" + currFunc->rname + ".dot") + (std::string(dstFileName) + ".dumpralloccon" + currFunc->rname + ".dot") .c_str()); std::string *name = new std::string[num_vertices(con)]; @@ -1216,27 +1384,36 @@ static void dump_con(const con_t &con) { // Dump cfg, with numbered nodes, show live variables at each node. static void dump_cfg(const cfg_t &cfg) { + if (!currFunc) + return; + std::ofstream dump_file( - (std::string(dstFileName) + ".dumpcfg" + currFunc->rname + ".dot") + (std::string(dstFileName) + ".dumpralloccfg" + currFunc->rname + ".dot") .c_str()); std::string *name = new std::string[num_vertices(cfg)]; for (unsigned int i = 0; i < boost::num_vertices(cfg); i++) { std::ostringstream os; os << i << ", " << cfg[i].ic->key << ": "; - std::set::const_iterator v; + cfg_alive_t::const_iterator v; for (v = cfg[i].alive.begin(); v != cfg[i].alive.end(); ++v) os << *v << " "; name[i] = os.str(); } - boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name)); + + boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name), + boost::default_writer(), + cfg_titlewriter(currFunc->rname, "register allocator")); delete[] name; } // Dump tree decomposition, show bag and live variables at each node. static void dump_tree_decomposition(const tree_dec_t &tree_dec) { + if (!currFunc) + return; + std::ofstream dump_file( - (std::string(dstFileName) + ".dumpdec" + currFunc->rname + ".dot") + (std::string(dstFileName) + ".dumprallocdec" + currFunc->rname + ".dot") .c_str()); unsigned int w = 0; @@ -1256,7 +1433,11 @@ static void dump_tree_decomposition(const tree_dec_t &tree_dec) { os << *v2 << " "; name[i] = os.str(); } - boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name)); + + boost::write_graphviz( + dump_file, tree_dec, boost::make_label_writer(name), + boost::default_writer(), + dec_titlewriter(w - 1, currFunc->rname, "register allocator")); delete[] name; #ifdef D_RALLOC_DEC diff --git a/src/SDCCsalloc.hpp b/src/SDCCsalloc.hpp new file mode 100644 index 000000000..0177c2ffa --- /dev/null +++ b/src/SDCCsalloc.hpp @@ -0,0 +1,496 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, +// 2011-2018 +// +// (c) 2011-2012 Goethe-Universität Frankfurt +// (c) 2018 Albert-Ludwigs-Universität Frankfurt +// +// 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 2, 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, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// A Chaitin-style stack allocator. + +#ifndef SDCCSALLOC_HH +#define SDCCSALLOC_HH 1 + +#include + +#include +#include + +extern "C" +{ +#include "SDCCmem.h" +#include "SDCCglobl.h" +} + +// #define DEBUG_SALLOC + +struct scon_node_t { + symbol *sym; + int color; + boost::icl::interval_set free_stack; + std::set> alignment_conflicts; +}; + +struct scon_edge_t { + bool alignment_conflict_only; +}; + +typedef boost::adjacency_list + scon_t; // Conflict graph for on-stack variables + +static bool clash(const symbol *s1, const symbol *s2) { + wassert(s1); + wassert(s2); + + if (!s1->isspilt && + !(IS_AGGREGATE(s1->type) || + s1->allocreq && + (s1->addrtaken || isVolatile(s1->type)))) // Spill location + { + for (const symbol *s = (const symbol *)setFirstItem(s1->usl.itmpStack); s; + s = (const symbol *)setNextItem(s1->usl.itmpStack)) + if (clash(s, s2)) + return (true); + return (false); + } + if (!s2->isspilt && + !(IS_AGGREGATE(s2->type) || + s2->allocreq && + (s2->addrtaken || isVolatile(s2->type)))) // Spill location + { + for (const symbol *s = (const symbol *)setFirstItem(s2->usl.itmpStack); s; + s = (const symbol *)setNextItem(s2->usl.itmpStack)) + if (clash(s1, s)) + return (true); + return (false); + } + + return (bitVectBitValue(s1->clashes, s2->key)); +} + +static var_t +var_from_operand(const std::map &symbol_to_sindex, + const operand *const op) { + if (!op || !IS_SYMOP(op)) + return (-1); + std::map::const_iterator si = + symbol_to_sindex.find(OP_SYMBOL_CONST(op)); + if (si == symbol_to_sindex.end()) + return (-1); + + return (si->second); +} + +template +static void set_spilt(G_t &G, const I_t &I, SI_t &scon) { + std::map symbol_to_sindex; + std::map iindex_to_sindex; + symbol *sym; + var_t j, j_mark; + + // Add variables that need to be on the stack due to having had their address + // taken (or for a few other reasons, such as being too large or too many to + // behandled by the register allocator). + for (sym = static_cast(setFirstItem(istack->syms)), j = 0; sym; + sym = static_cast(setNextItem(istack->syms))) { + if (sym->_isparm) + continue; + + // std::cout << "set_spilt() 1: Considering " << sym->name << "[" << + // sym->isspilt << ", " << IS_AGGREGATE(sym->type) << ", " << sym->allocreq + // << ", " << sym->addrtaken << "]\n"; + + if (/*!(IS_AGGREGATE(sym->type) || sym->allocreq && (sym->addrtaken || + isVolatile(sym->type)))*/ + sym->for_newralloc) + continue; + + if (!sym->isspilt && + !(IS_AGGREGATE(sym->type) || + sym->allocreq && + (sym->addrtaken || + isVolatile( + sym->type)))) // Looks like a spill location - check if it is + // already covered by live ranges below. + { + bool covered = true; + for (const symbol *s = (const symbol *)setFirstItem(sym->usl.itmpStack); + s; s = (const symbol *)setNextItem(sym->usl.itmpStack)) + if (!s->for_newralloc) { +#ifdef DEBUG_SALLOC + std::cout << "Adding " << sym->name << " for " << s->name << "(" << s + << ") to be allocated to stack. (" << s->for_newralloc + << ")\n"; + std::cout.flush(); +#endif + covered = false; + symbol_to_sindex[s] = j; + break; + } + if (covered) + continue; + } + + boost::add_vertex(scon); + symbol_to_sindex[sym] = j; + scon[j].sym = sym; + scon[j].color = -1; + j++; + } + j_mark = j; + + // Add edges due to scope (see C99 standard, verse 1233, which requires things + // to have different addresses, not allowing us to allocate them to the same + // location, even if we otherwise could). + for (unsigned int i = 0; i < boost::num_vertices(scon); i++) + for (unsigned int j = i + 1; j < boost::num_vertices(scon); j++) { + if (!(scon[i].sym->addrtaken) || !(scon[i].sym->addrtaken)) + continue; + short p = + btree_lowest_common_ancestor(scon[i].sym->block, scon[j].sym->block); + if (p == scon[i].sym->block || p == scon[j].sym->block) + boost::add_edge(i, j, scon); + } + + // Set stack live ranges + for (unsigned int i = 0; i < boost::num_vertices(G); i++) { + G[i].ic->localEscapeAlive = false; + + for (unsigned int j = 0; j < boost::num_vertices(scon); j++) { + short p = + btree_lowest_common_ancestor(G[i].ic->block, scon[j].sym->block); + if (p == G[i].ic->block || p == scon[j].sym->block) { + G[i].stack_alive.insert(j); + if (scon[j].sym->addrtaken || + IS_AGGREGATE(scon[j].sym->type)) // TODO: More accurate analysis. + G[i].ic->localEscapeAlive = true; + } + } + } + + // Add variables that have been spilt in register allocation. + for (unsigned int i = 0; i < boost::num_vertices(G); i++) { + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) { + var_t vs; + + symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + + if ((sym->regs[0] && !sym->isspilt) || sym->accuse || sym->remat || + !sym->nRegs || sym->usl.spillLoc && sym->usl.spillLoc->_isparm) + continue; + + if (iindex_to_sindex.find(I[*v].v) == iindex_to_sindex.end()) { + wassert(boost::add_vertex(scon) == j); + scon[j].sym = sym; + scon[j].color = -1; + iindex_to_sindex[I[*v].v] = j; + symbol_to_sindex[sym] = j; + j++; + } + + vs = iindex_to_sindex[I[*v].v]; + + G[i].stack_alive.insert(vs); // Needs to be allocated on the stack. + } + } + + // Add edges to conflict graph. + typename boost::graph_traits::edge_iterator e, e_end; + for (boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) { + if (I[boost::source(*e, I)].v == I[boost::target(*e, I)].v || + iindex_to_sindex.find(I[boost::source(*e, I)].v) == + iindex_to_sindex.end() || + iindex_to_sindex.find(I[boost::target(*e, I)].v) == + iindex_to_sindex.end()) + continue; + + boost::add_edge(iindex_to_sindex[I[boost::source(*e, I)].v], + iindex_to_sindex[I[boost::target(*e, I)].v], scon); + } + + // Add conflicts between variables that had their address taken and those that + // have been spilt by register allocation. + // TODO: More exact live range analysis for variables that had their address + // taken (to reduce stack space consumption further, by reducing the number of + // conflicts here). + for (unsigned int i = 0; i < j_mark; i++) + for (unsigned int j = 0; j < boost::num_vertices(scon); j++) { + if (i == j) + continue; + if (!scon[i].sym->isspilt && + !(IS_AGGREGATE(scon[i].sym->type) || + scon[i].sym->allocreq && + (scon[i].sym->addrtaken || + isVolatile(scon[i].sym->type)))) // Spill location + { + if (clash(scon[i].sym, scon[j].sym)) + boost::add_edge(i, j, scon); + continue; + } + short p = + btree_lowest_common_ancestor(scon[i].sym->block, scon[j].sym->block); + if (p == scon[i].sym->block || p == scon[j].sym->block) + boost::add_edge(i, j, scon); + } + + // Ugly hack: Regparms. + for (sym = static_cast(setFirstItem(istack->syms)), + j = boost::num_vertices(scon); + sym; sym = static_cast(setNextItem(istack->syms))) { + if (!sym->_isparm || !IS_REGPARM(sym->etype) || !sym->onStack || + !sym->allocreq) + continue; + + boost::add_vertex(scon); + scon[j].sym = sym; + scon[j].color = -1; + + // Extend liverange to cover everything. + for (unsigned int i = 0; i < boost::num_vertices(G); i++) + G[i].stack_alive.insert(j); + + // Conflict with everything. + for (unsigned int i = 0; i < j; i++) + boost::add_edge(i, j, scon); + + j++; + } + + // Edges for aligment conflict + typename SI_t::edge_iterator ei, ei_end; + for (boost::tie(ei, ei_end) = boost::edges(scon); ei != ei_end; ++ei) + scon[*ei].alignment_conflict_only = false; + for (unsigned int i = 0; i < boost::num_vertices(G); i++) { + const var_t result = var_from_operand(symbol_to_sindex, IC_RESULT(G[i].ic)); + + if (result < 0) + continue; + + const var_t left = var_from_operand(symbol_to_sindex, IC_LEFT(G[i].ic)); + const var_t right = var_from_operand(symbol_to_sindex, IC_RIGHT(G[i].ic)); + + if (left >= 0 && !boost::edge(result, left, scon).second) + scon[(boost::add_edge(result, left, scon)).first] + .alignment_conflict_only = true; + if (right >= 0 && !boost::edge(result, right, scon).second) + scon[(boost::add_edge(result, right, scon)).first] + .alignment_conflict_only = true; + } +} + +template +void color_stack_var(const var_t v, SI_t &SI, int start, int *ssize) { + symbol *const sym = SI[v].sym; + const int size = getSize(sym->type); + + SI[v].color = start; + + const int sloc = (port->stack.direction > 0) ? start : -start - size; + symbol *const ssym = + (sym->isspilt && sym->usl.spillLoc) ? sym->usl.spillLoc : sym; + + SPEC_STAK(ssym->etype) = ssym->stack = sloc; + + if (ssize) + *ssize = (start + size > *ssize) ? start + size : *ssize; + +#ifdef DEBUG_SALLOC + std::cout << "Placing " << sym->name << " (really " << ssym->name << ") at [" + << start << ", " << (start + size - 1) << "]\n"; + std::cout.flush(); +#endif + + // Mark stack location as used for all conflicting variables. + typename boost::graph_traits::adjacency_iterator n, n_end; + for (boost::tie(n, n_end) = boost::adjacent_vertices(v, SI); n != n_end; ++n) + if (!SI[boost::edge(v, *n, SI).first].alignment_conflict_only) + SI[*n].free_stack -= + boost::icl::discrete_interval::type(start, start + size); + else + SI[*n].alignment_conflicts.insert( + boost::icl::discrete_interval::type(start, start + size)); +} + +// Place a single variable on the stack greedily. +template +void color_stack_var_greedily(const var_t v, SI_t &SI, int alignment, + int *ssize) { + int start; + symbol *const sym = SI[v].sym; + const int size = getSize(sym->type); + + // Find a suitable free stack location. + boost::icl::interval_set::iterator si; + for (si = SI[v].free_stack.begin();; ++si) { + start = boost::icl::first(*si); + + bool alignment_issue; + do { + // Adjust start address for alignment conflict + std::set>::const_iterator ai, ai_end; + for (ai = SI[v].alignment_conflicts.begin(), + ai_end = SI[v].alignment_conflicts.end(); + ai != ai_end; ++ai) { + if (ai->upper() < start || ai->lower() > start + size - 1) + continue; + if (ai->lower() == start) + continue; + +#ifdef DEBUG_SALLOC + std::cerr << "Resolving alignment conflict at " << SI[v].sym->name + << "\n"; +#endif + + start = ai->upper() + 1; // Resolve conflict. + } + + // Adjust start address for alignment + alignment_issue = start % alignment; + if (start % alignment) + start = start + alignment - start % alignment; + } while (alignment_issue); + + if (boost::icl::last(*si) >= start + size - 1) + break; // Found one. + } + + color_stack_var(v, SI, start, ssize); +} + +static int get_alignment(sym_link *type) { +#if 1 + return (1); +#else + for (; IS_ARRAY(type); type = type->next) + ; + + switch (getSize(type)) { + case 0: // ? + case 1: + return (1); + case 2: + return (2); + case 3: + case 4: + return (4); + default: + return (8); + } +#endif +} + +template +void chaitin_ordering(const SI_t &SI, std::list &ordering) { + std::vector marked(boost::num_vertices(SI)); + unsigned int num_marked, i, d, mind, minn; + std::stack stack; + + for (num_marked = 0; num_marked < boost::num_vertices(SI); num_marked++) { + mind = UINT_MAX; + minn = -1; + for (i = 0; i < boost::num_vertices(SI); i++) { + if (marked[i]) + continue; + + typename boost::graph_traits::adjacency_iterator n, n_end; + for (boost::tie(n, n_end) = boost::adjacent_vertices(i, SI), d = 0; + n != n_end; ++n) + d += !marked[*n]; + + if (d < mind || + d == mind && + get_alignment(SI[i].sym->type) < + get_alignment( + SI[minn] + .sym->type)) // Coloring aligned variables first tends + // to keep gaps from alignment small. + { + mind = d; + minn = i; + } + } + + stack.push(minn); + marked[minn] = true; + } + + while (!stack.empty()) { + ordering.push_back(stack.top()); + stack.pop(); + } +} + +template void chaitin_salloc(SI_t &SI) { + std::list ordering; + + chaitin_ordering(SI, ordering); + + for (unsigned int i = 0; i < boost::num_vertices(SI); i++) + SI[i].free_stack.insert( + boost::icl::discrete_interval::type(0, 1 << 15)); + + int ssize = 0; + + clearStackOffsets(); + + std::list::const_iterator i, i_end; + for (i = ordering.begin(), i_end = ordering.end(); i != i_end; ++i) { + // Alignment, even when not required by the hardware helps avoid partially + // overlapping stack operands (which are not supported by code generation in + // some backends). + color_stack_var_greedily(*i, SI, get_alignment(SI[*i].sym->type), &ssize); + } + + if (currFunc) { + if (TARGET_PDK_LIKE && (ssize % 2)) // Padauk requires even stack alignment. + ssize++; +#ifdef DEBUG_SALLOC + std::cout << "Function " << currFunc->name << " currFunc->stack: old " + << currFunc->stack << ", new " << (currFunc->stack + ssize) + << "\n"; +#endif + currFunc->stack += ssize; + SPEC_STAK(currFunc->etype) += ssize; + } +} + +static void dump_scon(const scon_t &scon) { + if (!currFunc) + return; + + std::ofstream dump_file( + (std::string(dstFileName) + ".dumpsalloccon" + currFunc->rname + ".dot") + .c_str()); + + std::string *name = new std::string[boost::num_vertices(scon)]; + for (var_t i = 0; + static_cast::vertices_size_type>(i) < + boost::num_vertices(scon); + i++) { + int start = scon[i].color; + std::ostringstream os; + os << i; + if (scon[i].sym->name) + os << " : " << scon[i].sym->name << " : " << getSize(scon[i].sym->type) + << " [" << start << "," << (start + getSize(scon[i].sym->type) - 1) + << "]"; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, scon, boost::make_label_writer(name)); + delete[] name; +} +#endif diff --git a/src/SDCCset.c b/src/SDCCset.c index f27a56a4e..43c04f0c0 100644 --- a/src/SDCCset.c +++ b/src/SDCCset.c @@ -1,5 +1,5 @@ /*----------------------------------------------------------------- - SDCCset.c - contains support routines for sets + SDCCset.c - contains support routines for doubly linked lists. Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) @@ -143,9 +143,8 @@ void *addSet(set **list, void *item) { lp = *list = newSet(); } else { /* go to the end of the list */ - for (lp = *list; lp->next; lp = lp->next) { - //~ fprintf(stderr, ""); - } + for (lp = *list; lp->next; lp = lp->next) + ; lp = lp->next = newSet(); } if (!list) @@ -183,6 +182,35 @@ void deleteItemIf(set **sset, int (*cond)(void *, va_list), ...) { } } +/*-------------------------------------------------------------------*/ +/* destructItemIf - will delete from set if cond function returns 1, */ +/* upon deletion, item's destructor is also called */ +/*-------------------------------------------------------------------*/ +void destructItemIf(set **sset, void (*destructor)(void *item), + int (*cond)(void *, va_list), ...) { + set *sp = *sset; + va_list ap; + + while (sp) { + /* + * On the x86 va_list is just a pointer, so due to pass by value + * ap is not mofified by the called function. On the PPC va_list + * is a pointer to a structure, so ap is modified. Re-init each time. + */ + va_start(ap, cond); + + if ((*cond)(sp->item, ap)) { + destructor(sp->item); + deleteSetItem(sset, sp->item); + sp = *sset; + continue; + } + + va_end(ap); + sp = sp->next; + } +} + /*-----------------------------------------------------------------*/ /* deleteSetItem - will delete a given item from the list */ /*-----------------------------------------------------------------*/ @@ -215,6 +243,20 @@ void deleteSetItem(set **list, void *item) { /* could not find it */ } +/*-----------------------------------------------------------------*/ +/* replaceSetItem - will replace a given item in the list */ +/*-----------------------------------------------------------------*/ +void replaceSetItem(set *list, void *olditem, void *newitem) { + /* find the item in the list */ + for (; list; list = list->next) + if (list->item == olditem) { + list->item = newitem; + return; + } + + /* could not find it */ +} + /*-----------------------------------------------------------------*/ /* isinSet - the item is present in the linked list */ /*-----------------------------------------------------------------*/ @@ -263,9 +305,20 @@ set *unionSets(set *list1, set *list2, int throw) { set *un = NULL; set *lp; - /* add all elements in the first list */ - for (lp = list1; lp; lp = lp->next) - addSet(&un, lp->item); + /* If we were going to throw away the destination list */ + /* anyway, save memory and time by using it as the */ + /* starting point for the new list. */ + if (throw == THROW_DEST || throw == THROW_BOTH) { + un = list1; + if (throw == THROW_BOTH) + throw = THROW_SRC; + else + throw = THROW_NONE; + } else { + /* add all elements in the first list */ + for (lp = list1; lp; lp = lp->next) + addSet(&un, lp->item); + } /* now for all those in list2 which does not */ /* already exist in the list add */ diff --git a/src/SDCCset.h b/src/SDCCset.h index 01e21e247..a477927f0 100644 --- a/src/SDCCset.h +++ b/src/SDCCset.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------- - SDCCset.h - contains support routines for sets . + SDCCset.h - contains support routines for doubly linked lists. Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) @@ -50,7 +50,10 @@ void *addSet(set **, void *); void *addSetHead(set **, void *); void *getSet(set **); void deleteSetItem(set **, void *); +void replaceSetItem(set *, void *olditem, void *newitem); void deleteItemIf(set **, int (*cond)(void *, va_list), ...); +void destructItemIf(set **, void (*destructor)(void *), + int (*cond)(void *, va_list), ...); int isinSet(const set *, const void *); typedef int (*insetwithFunc)(void *, void *); int isinSetWith(set *, void *, insetwithFunc cfunc); diff --git a/src/SDCCsymt.c b/src/SDCCsymt.c index 8fb4fec56..010a38128 100644 --- a/src/SDCCsymt.c +++ b/src/SDCCsymt.c @@ -123,7 +123,7 @@ int hashKey(const char *s) { /*-----------------------------------------------------------------*/ /* addSym - adds a symbol to the hash Table */ /*-----------------------------------------------------------------*/ -void addSym(bucket **stab, void *sym, char *sname, int level, int block, +void addSym(bucket **stab, void *sym, char *sname, long level, int block, int checkType) { int i; /* index into the hash Table */ bucket *bp; /* temp bucket * */ @@ -267,7 +267,7 @@ void *findSymWithLevel(bucket **stab, symbol *sym) { /*-----------------------------------------------------------------*/ /* findSymWithBlock - finds a symbol with name in a block */ /*-----------------------------------------------------------------*/ -void *findSymWithBlock(bucket **stab, symbol *sym, int block) { +void *findSymWithBlock(bucket **stab, symbol *sym, int block, long level) { bucket *bp; if (!sym) @@ -275,7 +275,8 @@ void *findSymWithBlock(bucket **stab, symbol *sym, int block) { bp = stab[hashKey(sym->name)]; while (bp) { - if (strcmp(bp->name, sym->name) == 0 && bp->block <= block) + if (strcmp(bp->name, sym->name) == 0 && + (bp->block == block || (bp->block < block && bp->level < level))) break; bp = bp->next; } @@ -286,7 +287,7 @@ void *findSymWithBlock(bucket **stab, symbol *sym, int block) { /*------------------------------------------------------------------*/ /* newSymbol () - returns a new pointer to a symbol */ /*------------------------------------------------------------------*/ -symbol *newSymbol(const char *name, int scope) { +symbol *newSymbol(const char *name, long scope) { symbol *sym; sym = Safe_alloc(sizeof(symbol)); @@ -299,6 +300,12 @@ symbol *newSymbol(const char *name, int scope) { sym->fileDef = lexFilename; sym->for_newralloc = 0; sym->isinscope = 1; + sym->usl.spillLoc = 0; + + // Err on the safe side, when in doubt disabling optimizations. + sym->funcDivFlagSafe = 0; + sym->funcUsesVolatile = 1; + return sym; } @@ -310,6 +317,7 @@ sym_link *newLink(SYM_LINK_CLASS select) { p = Safe_alloc(sizeof(sym_link)); p->xclass = select; + p->funcAttrs.z88dk_params_offset = 0; return p; } @@ -434,16 +442,13 @@ void pointerTypes(sym_link *ptr, sym_link *type) { /* addDecl - adds a declarator @ the end of a chain */ /*------------------------------------------------------------------*/ void addDecl(symbol *sym, int type, sym_link *p) { - static sym_link *empty = NULL; sym_link *head; sym_link *tail; sym_link *t; if (getenv("SDCC_DEBUG_FUNCTION_POINTERS")) - fprintf(stderr, "SDCCsymt.c:addDecl(%s,%d,%p)\n", sym->name, type, p); - - if (empty == NULL) - empty = newLink(SPECIFIER); + fprintf(stderr, "SDCCsymt.c:addDecl(%s,%d,%p)\n", sym->name, type, + (void *)p); /* if we are passed a link then set head & tail */ if (p) { @@ -455,29 +460,48 @@ void addDecl(symbol *sym, int type, sym_link *p) { DCL_TYPE(head) = type; } - /* if this is the first entry */ + // no type yet: make p the type if (!sym->type) { sym->type = head; sym->etype = tail; - } else if (IS_SPEC(sym->etype) && IS_SPEC(head) && head == tail) { + } + // type ends in spec, p is single spec element: merge specs + else if (IS_SPEC(sym->etype) && IS_SPEC(head) && head == tail) { sym->etype = mergeSpec(sym->etype, head, sym->name); - } else if (IS_SPEC(sym->etype) && !IS_SPEC(head) && head == tail) { + } + // type ends in spec, p is single decl element: p goes before spec + else if (IS_SPEC(sym->etype) && !IS_SPEC(head) && head == tail) { t = sym->type; while (t->next != sym->etype) t = t->next; t->next = head; tail->next = sym->etype; - } else if (IS_FUNC(sym->type) && IS_SPEC(sym->type->next) && - !memcmp(sym->type->next, empty, sizeof(sym_link))) { - sym->type->next = head; - sym->etype = tail; - } else { + } + // type ends in spec, p ends in spec: merge specs, p's decls go before spec + else if (IS_SPEC(sym->etype) && IS_SPEC(tail)) { + sym->etype = mergeSpec(sym->etype, tail, sym->name); + + // cut off p's spec + t = head; + while (t->next != tail) + t = t->next; + tail = t; + + // splice p's decls + t = sym->type; + while (t->next != sym->etype) + t = t->next; + t->next = head; + tail->next = sym->etype; + } + // append p to the type + else { sym->etype->next = head; sym->etype = tail; } /* if the type is an unknown pointer and has - a tspec then take the storage class const & volatile + a tspec then take the storage class and address attribute from the tspec & make it those of this symbol */ if (p && !IS_SPEC(p) && @@ -524,7 +548,7 @@ void checkTypeSanity(sym_link *etype, const char *name) { noun = nounName(etype); if (getenv("DEBUG_SANITY")) { - fprintf(stderr, "checking sanity for %s %p\n", name, etype); + fprintf(stderr, "checking sanity for %s %p\n", name, (void *)etype); } if ((SPEC_NOUN(etype) == V_BOOL || SPEC_NOUN(etype) == V_CHAR || @@ -542,17 +566,14 @@ void checkTypeSanity(sym_link *etype, const char *name) { werror(E_SIGNED_OR_UNSIGNED_INVALID, noun, name); } - // special case for "short" - if (SPEC_SHORT(etype)) { - SPEC_NOUN(etype) = options.shortis8bits ? V_CHAR : V_INT; - SPEC_SHORT(etype) = 0; - } - /* if no noun e.g. "const a;" or "data b;" or "signed s" or "long l" assume an int */ if (!SPEC_NOUN(etype)) { SPEC_NOUN(etype) = V_INT; + if (!(SPEC_SHORT(etype) || SPEC_LONG(etype) || SPEC_LONGLONG(etype) || + SPEC_SIGN(etype) || SPEC_USIGN(etype))) + werror(options.std_c99 ? E_NO_TYPE_SPECIFIER : W_NO_TYPE_SPECIFIER, name); } /* ISO/IEC 9899 J.3.9 implementation defined behaviour: */ @@ -574,15 +595,15 @@ void checkTypeSanity(sym_link *etype, const char *name) { /* finalizeSpec */ /* currently just a V_CHAR is forced to be unsigned */ /* when it's neither signed nor unsigned */ -/* and the --funsigned-char command line switch is active */ +/* unless the --fsigned-char command line switch is active */ /*------------------------------------------------------------------*/ sym_link *finalizeSpec(sym_link *lnk) { - if (options.unsigned_char) { - sym_link *p = lnk; - while (p && !IS_SPEC(p)) - p = p->next; - if (SPEC_NOUN(p) == V_CHAR && !SPEC_USIGN(p) && !p->select.s.b_signed) - SPEC_USIGN(p) = 1; + sym_link *p = lnk; + while (p && !IS_SPEC(p)) + p = p->next; + if (SPEC_NOUN(p) == V_CHAR && !SPEC_USIGN(p) && !p->select.s.b_signed) { + SPEC_USIGN(p) = !options.signed_char; + p->select.s.b_implicit_sign = true; } return lnk; } @@ -591,6 +612,8 @@ sym_link *finalizeSpec(sym_link *lnk) { /* mergeSpec - merges two specifiers and returns the new one */ /*------------------------------------------------------------------*/ sym_link *mergeSpec(sym_link *dest, sym_link *src, const char *name) { + unsigned int i; + if (!IS_SPEC(dest) || !IS_SPEC(src)) { #if 0 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "cannot merge declarator"); @@ -602,6 +625,27 @@ sym_link *mergeSpec(sym_link *dest, sym_link *src, const char *name) { #endif } + if (!options.std_c11 && !options.std_c99) { + if (SPEC_SIGN(dest) && SPEC_SIGN(src)) + werror(W_REPEAT_QUALIFIER, "signed"); + if (SPEC_USIGN(dest) && SPEC_USIGN(src)) + werror(W_REPEAT_QUALIFIER, "unsigned"); + if (SPEC_CONST(dest) && SPEC_CONST(src)) + werror(W_REPEAT_QUALIFIER, "const"); + if (SPEC_VOLATILE(dest) && SPEC_VOLATILE(src)) + werror(W_REPEAT_QUALIFIER, "volatile"); + if (SPEC_STAT(dest) && SPEC_STAT(src)) + werror(W_REPEAT_QUALIFIER, "static"); + if (SPEC_EXTR(dest) && SPEC_EXTR(src)) + werror(W_REPEAT_QUALIFIER, "extern"); + if (SPEC_TYPEDEF(dest) && SPEC_TYPEDEF(src)) + werror(W_REPEAT_QUALIFIER, "typedef"); + if (SPEC_SCLS(dest) == S_REGISTER && SPEC_SCLS(src) == S_REGISTER) + werror(W_REPEAT_QUALIFIER, "register"); + if (SPEC_SCLS(dest) == S_AUTO && SPEC_SCLS(src) == S_AUTO) + werror(W_REPEAT_QUALIFIER, "auto"); + } + if (SPEC_NOUN(src)) { if (!SPEC_NOUN(dest)) { SPEC_NOUN(dest) = SPEC_NOUN(src); @@ -676,9 +720,15 @@ sym_link *mergeSpec(sym_link *dest, sym_link *src, const char *name) { if (SPEC_ARGREG(src) && !SPEC_ARGREG(dest)) SPEC_ARGREG(dest) = SPEC_ARGREG(src); + if (SPEC_STAT(dest) && SPEC_EXTR(dest)) + werror(E_TWO_OR_MORE_STORAGE_CLASSES, name); + if (IS_STRUCT(dest) && SPEC_STRUCT(dest) == NULL) SPEC_STRUCT(dest) = SPEC_STRUCT(src); + if (FUNC_ISISR(dest) && FUNC_ISISR(src)) + werror(E_INT_MULTIPLE, name); + /* these are the only function attributes that will be set in a specifier while parsing */ FUNC_NONBANKED(dest) |= FUNC_NONBANKED(src); @@ -694,6 +744,11 @@ sym_link *mergeSpec(sym_link *dest, sym_link *src, const char *name) { FUNC_REGBANK(dest) |= FUNC_REGBANK(src); FUNC_ISINLINE(dest) |= FUNC_ISINLINE(src); FUNC_ISNORETURN(dest) |= FUNC_ISNORETURN(src); + FUNC_ISSMALLC(dest) |= FUNC_ISSMALLC(src); + FUNC_ISZ88DK_FASTCALL(dest) |= FUNC_ISZ88DK_FASTCALL(src); + FUNC_ISZ88DK_CALLEE(dest) |= FUNC_ISZ88DK_CALLEE(src); + for (i = 0; i < 9; i++) + dest->funcAttrs.preserved_regs[i] |= src->funcAttrs.preserved_regs[i]; if (SPEC_ADDRSPACE(src) && SPEC_ADDRSPACE(dest)) werror(E_TWO_OR_MORE_STORAGE_CLASSES, name); @@ -732,17 +787,22 @@ sym_link *mergeDeclSpec(sym_link *dest, sym_link *src, const char *name) { } } - DCL_PTR_CONST(decl) |= SPEC_CONST(spec); - DCL_PTR_VOLATILE(decl) |= SPEC_VOLATILE(spec); - DCL_PTR_RESTRICT(decl) |= SPEC_RESTRICT(spec); - if (DCL_PTR_ADDRSPACE(decl) && SPEC_ADDRSPACE(spec) && - strcmp(DCL_PTR_ADDRSPACE(decl)->name, SPEC_ADDRSPACE(spec)->name)) - werror(E_SYNTAX_ERROR, yytext); - if (SPEC_ADDRSPACE(spec)) - DCL_PTR_ADDRSPACE(decl) = SPEC_ADDRSPACE(spec); + // for pointers, type qualifiers go in the declarator + if (DCL_TYPE(decl) != ARRAY && DCL_TYPE(decl) != FUNCTION) { + DCL_PTR_CONST(decl) |= SPEC_CONST(spec); + DCL_PTR_VOLATILE(decl) |= SPEC_VOLATILE(spec); + DCL_PTR_RESTRICT(decl) |= SPEC_RESTRICT(spec); + if (DCL_PTR_ADDRSPACE(decl) && SPEC_ADDRSPACE(spec) && + strcmp(DCL_PTR_ADDRSPACE(decl)->name, SPEC_ADDRSPACE(spec)->name)) + werror(E_SYNTAX_ERROR, yytext); + if (SPEC_ADDRSPACE(spec)) + DCL_PTR_ADDRSPACE(decl) = SPEC_ADDRSPACE(spec); - SPEC_CONST(spec) = SPEC_VOLATILE(spec) = SPEC_RESTRICT(spec) = 0; - SPEC_ADDRSPACE(spec) = 0; + SPEC_CONST(spec) = 0; + SPEC_VOLATILE(spec) = 0; + SPEC_RESTRICT(spec) = 0; + SPEC_ADDRSPACE(spec) = 0; + } lnk = decl; while (lnk && !IS_SPEC(lnk->next)) @@ -754,7 +814,7 @@ sym_link *mergeDeclSpec(sym_link *dest, sym_link *src, const char *name) { /*-------------------------------------------------------------------*/ /* genSymName - generates and returns a name used for anonymous vars */ /*-------------------------------------------------------------------*/ -char *genSymName(int level) { +char *genSymName(long level) { static int gCount = 0; static char gname[SDCC_NAME_MAX + 1]; @@ -780,6 +840,7 @@ sym_link *newCharLink() { p = newLink(SPECIFIER); SPEC_NOUN(p) = V_CHAR; + SPEC_USIGN(p) = 1; return p; } @@ -921,12 +982,14 @@ unsigned int getSize(sym_link *p) { case IPOINTER: case PPOINTER: case POINTER: - return (PTRSIZE); + return (NEARPTRSIZE); case EEPPOINTER: case FPOINTER: case CPOINTER: + if (!IS_FUNCPTR(p)) + return (FARPTRSIZE); case FUNCTION: - return (FPTRSIZE); + return (IFFUNC_ISBANKEDCALL(p) ? BFUNCPTRSIZE : FUNCPTRSIZE); case GPOINTER: return (GPTRSIZE); @@ -1024,12 +1087,12 @@ unsigned int bitsForType(sym_link *p) { case IPOINTER: case PPOINTER: case POINTER: - return (PTRSIZE * 8); + return (NEARPTRSIZE * 8); case EEPPOINTER: case FPOINTER: case CPOINTER: case FUNCTION: - return (FPTRSIZE * 8); + return (FARPTRSIZE * 8); case GPOINTER: return (GPTRSIZE * 8); default: @@ -1040,7 +1103,7 @@ unsigned int bitsForType(sym_link *p) { /*------------------------------------------------------------------*/ /* copySymbolChain - copies a symbol chain */ /*------------------------------------------------------------------*/ -symbol *copySymbolChain(symbol *src) { +symbol *copySymbolChain(const symbol *src) { symbol *dest; if (!src) @@ -1054,7 +1117,7 @@ symbol *copySymbolChain(symbol *src) { /*------------------------------------------------------------------*/ /* copySymbol - makes a copy of a symbol */ /*------------------------------------------------------------------*/ -symbol *copySymbol(symbol *src) { +symbol *copySymbol(const symbol *src) { symbol *dest; if (!src) @@ -1146,9 +1209,10 @@ void addSymChain(symbol **symHead) { getNelements(sym->type, sym->ival); } - /* if already exists in the symbol table on the same level */ + /* if already exists in the symbol table on the same level, ignoring + * sublevels */ if ((csym = findSymWithLevel(SymbolTab, sym)) && - csym->level == sym->level) { + csym->level / LEVEL_UNIT == sym->level / LEVEL_UNIT) { /* if not formal parameter and not in file scope then show symbol redefined error else check if symbols have compatible types */ @@ -1323,6 +1387,7 @@ int compStructSize(int su, structdef *sdef) { int sum = 0, usum = 0; int bitOffset = 0; symbol *loop; + const int oldlineno = lineno; if (!sdef->fields) { werror(E_UNKNOWN_SIZE, sdef->tag); @@ -1331,6 +1396,8 @@ int compStructSize(int su, structdef *sdef) { /* for the identifiers */ loop = sdef->fields; while (loop) { + lineno = loop->lineDef; + /* create the internal name for this variable */ SNPRINTF(loop->rname, sizeof(loop->rname), "_%s", loop->name); if (su == UNION) { @@ -1344,8 +1411,26 @@ int compStructSize(int su, structdef *sdef) { SPEC_BUNNAMED(loop->etype) = loop->bitUnnamed; /* change it to a unsigned bit */ - SPEC_NOUN(loop->etype) = - SPEC_NOUN(loop->etype) == V_BOOL ? V_BBITFIELD : V_BITFIELD; + switch (SPEC_NOUN(loop->etype)) { + case V_BOOL: + SPEC_NOUN(loop->etype) = V_BBITFIELD; + if (loop->bitVar > 1) + werror(E_BITFLD_SIZE, 1); + break; + case V_CHAR: + SPEC_NOUN(loop->etype) = V_BITFIELD; + if (loop->bitVar > 8) + werror(E_BITFLD_SIZE, 8); + break; + case V_INT: + SPEC_NOUN(loop->etype) = V_BITFIELD; + if (loop->bitVar > port->s.int_size * 8) + werror(E_BITFLD_SIZE, port->s.int_size * 8); + break; + default: + werror(E_BITFLD_TYPE); + } + /* ISO/IEC 9899 J.3.9 implementation defined behaviour: */ /* a "plain" int bitfield is unsigned */ if (!loop->etype->select.s.b_signed) @@ -1412,10 +1497,10 @@ int compStructSize(int su, structdef *sdef) { sdef->b_flexArrayMember = TRUE; /* is another struct-member following? */ if (loop->next) - werror(E_FLEXARRAY_NOTATEND); + werror(E_FLEXARRAY_NOTATEND, loop->name); /* is it the first struct-member? */ else if (loop == sdef->fields) - werror(E_FLEXARRAY_INEMPTYSTRCT); + werror(E_FLEXARRAY_INEMPTYSTRCT, loop->name); } else if (ret == INCOMPLETE) { werror(E_INCOMPLETE_FIELD, loop->name); } @@ -1436,6 +1521,8 @@ int compStructSize(int su, structdef *sdef) { if (su != UNION) sum += ((bitOffset + 7) / 8); + lineno = oldlineno; + return (su == UNION ? usum : sum); } @@ -1460,6 +1547,11 @@ void promoteAnonStructs(int su, structdef *sdef) { /* with the fields it contains and adjust all */ /* the offsets */ + /* tagged anonymous struct/union is rejected here, though gcc allow it */ + if (SPEC_STRUCT(field->type)->tagsym != NULL) + werrorfl(field->fileDef, field->lineDef, E_ANONYMOUS_STRUCT_TAG, + SPEC_STRUCT(field->type)->tag); + base = field->offset; subfield = copySymbolChain(SPEC_STRUCT(field->type)->fields); if (!subfield) @@ -1502,6 +1594,11 @@ static void checkSClass(symbol *sym, int isProto) { fprintf(stderr, "checkSClass: %s \n", sym->name); } + if (!sym->level && SPEC_SCLS(sym->etype) == S_AUTO) { + werrorfl(sym->fileDef, sym->lineDef, E_AUTO_FILE_SCOPE); + SPEC_SCLS(sym->etype) = S_FIXED; + } + /* type is literal can happen for enums change to auto */ if (SPEC_SCLS(sym->etype) == S_LITERAL && !SPEC_ENUM(sym->etype)) SPEC_SCLS(sym->etype) = S_AUTO; @@ -1511,6 +1608,11 @@ static void checkSClass(symbol *sym, int isProto) { SPEC_VOLATILE(sym->etype) = 1; } + if (SPEC_NEEDSPAR(sym->etype)) { + werrorfl(sym->fileDef, sym->lineDef, E_QUALIFIED_ARRAY_NOPARAM); + SPEC_NEEDSPAR(sym->etype) = 0; + } + /* make sure restrict is only used with pointers */ if (SPEC_RESTRICT(sym->etype)) { werrorfl(sym->fileDef, sym->lineDef, E_BAD_RESTRICT); @@ -1519,7 +1621,7 @@ static void checkSClass(symbol *sym, int isProto) { t = sym->type; while (t) { - if (IS_DECL(t) && DCL_PTR_RESTRICT(t) && !IS_PTR(t)) { + if (IS_DECL(t) && DCL_PTR_RESTRICT(t) && !(IS_PTR(t) && !IS_FUNCPTR(t))) { werrorfl(sym->fileDef, sym->lineDef, E_BAD_RESTRICT); DCL_PTR_RESTRICT(t) = 0; break; @@ -1529,8 +1631,19 @@ static void checkSClass(symbol *sym, int isProto) { /* if absolute address given then it mark it as volatile -- except in the PIC port */ - if (IS_ABSOLUTE(sym->etype)) - SPEC_VOLATILE(sym->etype) = 1; + +#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16 + /* The PIC port uses a different peep hole optimizer based on "pCode" */ + if (!TARGET_PIC_LIKE) +#endif + + if (IS_ABSOLUTE(sym->etype)) + SPEC_VOLATILE(sym->etype) = 1; + + if (IS_ABSOLUTE(sym->etype) && SPEC_SCLS(sym->etype) == S_SFR) { + if (SPEC_ADDR(sym->etype) > (FUNC_REGBANK(sym->type) ? 0xffff : 0xff)) + werror(W_SFR_ABSRANGE, sym->name); + } /* If code memory is read only, then pointers to code memory */ /* implicitly point to constants -- make this explicit */ @@ -1577,7 +1690,7 @@ static void checkSClass(symbol *sym, int isProto) { } /* if this is an automatic symbol */ - if (sym->level && (options.stackAuto || reentrant)) { + if (sym->level && (options.stackAuto)) { if (SPEC_SCLS(sym->etype) != S_BIT && SPEC_SCLS(sym->etype) != S_REGISTER) { if ((SPEC_SCLS(sym->etype) == S_AUTO || SPEC_SCLS(sym->etype) == S_FIXED || @@ -1596,7 +1709,7 @@ static void checkSClass(symbol *sym, int isProto) { /* automatic symbols cannot be given */ /* an absolute address ignore it */ if (sym->level && !IS_STATIC(sym->etype) && SPEC_ABSA(sym->etype) && - (options.stackAuto || reentrant)) { + (options.stackAuto)) { werror(E_AUTO_ABSA, sym->name); SPEC_ABSA(sym->etype) = 0; } @@ -1604,7 +1717,7 @@ static void checkSClass(symbol *sym, int isProto) { if (sym->level && !IS_STATIC(sym->etype) && (IS_DECL(sym->type) ? DCL_PTR_ADDRSPACE(sym->type) : SPEC_ADDRSPACE(sym->type)) && - (options.stackAuto || reentrant)) { + (options.stackAuto)) { werror(E_AUTO_ADDRSPACE, sym->name); if (IS_DECL(sym->type)) DCL_PTR_ADDRSPACE(sym->type) = 0; @@ -1636,10 +1749,9 @@ static void checkSClass(symbol *sym, int isProto) { if (!isProto) { /* variables declared in CODE space must have */ - /* initializers if not an extern */ + /* initializers if not an extern, a global or a static */ if (SPEC_SCLS(sym->etype) == S_CODE && sym->ival == NULL && !sym->_isparm && - //! sym->level && - port->mem.code_ro && !IS_EXTERN(sym->etype) && !SPEC_ABSA(sym->etype) && + IS_AUTO(sym) && port->mem.code_ro && !SPEC_ABSA(sym->etype) && !funcInChain(sym->type)) werror(E_CODE_NO_INIT, sym->name); } @@ -1666,7 +1778,8 @@ void changePointer(sym_link *p) { if (IS_DECL(p) && DCL_TYPE(p) == UPOINTER) DCL_TYPE(p) = port->unqualified_pointer; if (IS_PTR(p) && IS_FUNC(p->next)) - DCL_TYPE(p) = CPOINTER; + if (!IFFUNC_ISBANKEDCALL(p->next)) + DCL_TYPE(p) = CPOINTER; } } @@ -1688,8 +1801,9 @@ int checkDecl(symbol *sym, int isProto) { /*------------------------------------------------------------------*/ /* copyLinkChain - makes a copy of the link chain & rets ptr 2 head */ /*------------------------------------------------------------------*/ -sym_link *copyLinkChain(sym_link *p) { - sym_link *head, *curr, *loop; +sym_link *copyLinkChain(const sym_link *p) { + sym_link *head, *loop; + const sym_link *curr; /* note: v_struct and v_struct->fields are not copied! */ curr = p; @@ -1726,7 +1840,7 @@ void cleanUpBlock(bucket **table, int block) { /* cleanUpLevel - cleans up the symbol table specified for all the */ /* symbols in the given level */ /*------------------------------------------------------------------*/ -void cleanUpLevel(bucket **table, int level) { +void cleanUpLevel(bucket **table, long level) { int i; bucket *chain; @@ -1741,6 +1855,9 @@ void cleanUpLevel(bucket **table, int level) { } symbol *getAddrspace(sym_link *type) { + while (IS_ARRAY(type)) + type = type->next; + if (IS_DECL(type)) return (DCL_PTR_ADDRSPACE(type)); return (SPEC_ADDRSPACE(type)); @@ -1814,6 +1931,21 @@ sym_link *computeType(sym_link *type1, sym_link *type2, RESULT_TYPE resultType, /* Conditional operator has some special type conversion rules */ if (op == ':') { + /* Function types are really pointers to functions */ + if (IS_FUNC(type1)) { + sym_link *fptr; + fptr = newLink(DECLARATOR); + DCL_TYPE(fptr) = CPOINTER; + fptr->next = type1; + type1 = fptr; + } + if (IS_FUNC(type2)) { + sym_link *fptr; + fptr = newLink(DECLARATOR); + DCL_TYPE(fptr) = CPOINTER; + fptr->next = type2; + type2 = fptr; + } /* If either type is an array, convert to pointer */ if (IS_ARRAY(type1)) { value *val = aggregateToPointer(valFromType(type1)); @@ -1848,8 +1980,12 @@ sym_link *computeType(sym_link *type1, sym_link *type2, RESULT_TYPE resultType, /* Otherwise fall through to the general case */ } + /* shift operators have the important type in the left operand */ + if (op == LEFT_OP || op == RIGHT_OP) + rType = copyLinkChain(type1); + /* if one of them is a pointer or array then that prevails */ - if (IS_PTR(type1) || IS_ARRAY(type1)) + else if (IS_PTR(type1) || IS_ARRAY(type1)) rType = copyLinkChain(type1); else if (IS_PTR(type2) || IS_ARRAY(type2)) rType = copyLinkChain(type2); @@ -1914,7 +2050,7 @@ sym_link *computeType(sym_link *type1, sym_link *type2, RESULT_TYPE resultType, // fallthrough case RESULT_TYPE_BOOL: if (op == ':') { - SPEC_NOUN(reType) = V_BIT; + SPEC_NOUN(reType) = V_BOOL; return rType; } break; @@ -2040,6 +2176,7 @@ int compareFuncType(sym_link *dest, sym_link *src) { value *exargs, *acargs; value *checkValue; int argCnt = 0; + int i; /* if not type then some kind of error */ if (!dest || !src) @@ -2066,6 +2203,14 @@ int compareFuncType(sym_link *dest, sym_link *src) { return 0; } + if (IFFUNC_ISZ88DK_FASTCALL(dest) != IFFUNC_ISZ88DK_FASTCALL(src) || + IFFUNC_ISZ88DK_CALLEE(dest) != IFFUNC_ISZ88DK_CALLEE(src)) + return 0; + + for (i = 0; i < 9; i++) + if (dest->funcAttrs.preserved_regs[i] > src->funcAttrs.preserved_regs[i]) + return 0; + /* compare register bank */ if (FUNC_REGBANK(dest) != FUNC_REGBANK( @@ -2125,12 +2270,18 @@ int comparePtrType(sym_link *dest, sym_link *src, bool bMustCast) { (!IS_VOID(src->next) && IS_VOID(dest->next))) return -1; res = compareType(dest->next, src->next); - if (res == 1) + + /* All function pointers can be cast (6.6 in the ISO C11 standard) TODO: What + * about address spaces? */ + if (res == 0 && !bMustCast && IS_DECL(src) && IS_FUNC(src->next) && + IS_DECL(dest) && IS_FUNC(dest->next)) + return -1; + else if (res == 1) return bMustCast ? -1 : 1; else if (res == -2) return bMustCast ? -1 : -2; else - return 0; + return res; } /*--------------------------------------------------------------------*/ @@ -2237,6 +2388,10 @@ int compareType(sym_link *dest, sym_link *src) { else return 1; } + + if (SPEC_SHORT(dest) != SPEC_SHORT(src)) + return -1; + if (SPEC_LONG(dest) != SPEC_LONG(src)) return -1; @@ -2252,7 +2407,7 @@ int compareType(sym_link *dest, sym_link *src) { /*--------------------------------------------------------------------*/ /* compareTypeExact - will do type check return 1 if match exactly */ /*--------------------------------------------------------------------*/ -int compareTypeExact(sym_link *dest, sym_link *src, int level) { +int compareTypeExact(sym_link *dest, sym_link *src, long level) { STORAGE_CLASS srcScls, destScls; if (!dest && !src) @@ -2393,12 +2548,17 @@ int compareTypeExact(sym_link *dest, sym_link *src, int level) { destScls = (options.useXstack ? S_XSTACK : S_STACK); if (srcScls == S_FIXED) srcScls = (options.useXstack ? S_XSTACK : S_STACK); + } else if (options.useXstack) { + if (destScls == S_FIXED) + destScls = S_XDATA; + if (srcScls == S_FIXED) + srcScls = S_XDATA; } } if (srcScls != destScls) { #if 0 - printf ("level = %d\n", level); + printf ("level = %ld:%ld\n", level / LEVEL_UNIT, level % LEVEL_UNIT); printf ("SPEC_SCLS (src) = %d, SPEC_SCLS (dest) = %d\n", SPEC_SCLS (src), SPEC_SCLS (dest)); printf ("srcScls = %d, destScls = %d\n", srcScls, destScls); #endif @@ -2409,7 +2569,8 @@ int compareTypeExact(sym_link *dest, sym_link *src, int level) { } /*---------------------------------------------------------------------------*/ -/* compareTypeExact - will do type check return 1 if representation is same. */ +/* compareTypeInexact - will do type check return 1 if representation is same. + */ /* Useful for redundancy elimination. */ /*---------------------------------------------------------------------------*/ int compareTypeInexact(sym_link *dest, sym_link *src) { @@ -2519,7 +2680,7 @@ int checkFunction(symbol *sym, symbol *csym) { } if (!IS_FUNC(sym->type)) { - werror(E_SYNTAX_ERROR, sym->name); + werrorfl(sym->fileDef, sym->lineDef, E_SYNTAX_ERROR, sym->name); return 0; } @@ -2544,6 +2705,12 @@ int checkFunction(symbol *sym, symbol *csym) { if (!sym->type->next) sym->type->next = sym->etype = newIntLink(); + /* function cannot return aggregate */ + if (IS_AGGREGATE(sym->type->next)) { + werrorfl(sym->fileDef, sym->lineDef, E_FUNC_AGGR, sym->name); + return 0; + } + /* check if this function is defined as calleeSaves then mark it as such */ FUNC_CALLEESAVES(sym->type) = inCalleeSaveList(sym->name); @@ -2552,20 +2719,21 @@ int checkFunction(symbol *sym, symbol *csym) { /* then it cannot have arguments */ if (IFFUNC_ARGS(sym->type) && FUNC_ISISR(sym->type)) { if (!IS_VOID(FUNC_ARGS(sym->type)->type)) { - werror(E_INT_ARGS, sym->name); + werrorfl(sym->fileDef, sym->lineDef, E_INT_ARGS, sym->name); FUNC_ARGS(sym->type) = NULL; } } if (IFFUNC_ISSHADOWREGS(sym->type) && !FUNC_ISISR(sym->type)) { - werror(E_SHADOWREGS_NO_ISR, sym->name); + werrorfl(sym->fileDef, sym->lineDef, E_SHADOWREGS_NO_ISR, sym->name); } for (argCnt = 1, acargs = FUNC_ARGS(sym->type); acargs; acargs = acargs->next, argCnt++) { if (!acargs->sym) { // this can happen for reentrant functions - werror(E_PARAM_NAME_OMITTED, sym->name, argCnt); + werrorfl(sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, + argCnt); // the show must go on: synthesize a name and symbol SNPRINTF(acargs->name, sizeof(acargs->name), "_%s_PARM_%d", sym->name, argCnt); @@ -2577,7 +2745,8 @@ int checkFunction(symbol *sym, symbol *csym) { strncpyz(acargs->sym->rname, acargs->name, sizeof(acargs->sym->rname)); } else if (strcmp(acargs->sym->name, acargs->sym->rname) == 0) { // synthesized name - werror(E_PARAM_NAME_OMITTED, sym->name, argCnt); + werrorfl(sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, + argCnt); } } argCnt--; @@ -2590,37 +2759,39 @@ int checkFunction(symbol *sym, symbol *csym) { /* check if body already present */ if (csym && IFFUNC_HASBODY(csym->type)) { - werror(E_FUNC_BODY, sym->name); + werrorfl(sym->fileDef, sym->lineDef, E_FUNC_BODY, sym->name); return 0; } /* check the return value type */ if (compareType(csym->type, sym->type) <= 0) { - werror(E_PREV_DEF_CONFLICT, csym->name, "type"); + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "type", csym->fileDef, csym->lineDef); printFromToType(csym->type, sym->type); return 0; } - if (FUNC_ISISR(csym->type) != FUNC_ISISR(sym->type)) { - werror(E_PREV_DEF_CONFLICT, csym->name, "interrupt"); - } + if (FUNC_ISISR(csym->type) != FUNC_ISISR(sym->type)) + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "interrupt", csym->fileDef, csym->lineDef); /* I don't think this is necessary for interrupts. An isr is a */ /* root in the calling tree. */ if ((FUNC_REGBANK(csym->type) != FUNC_REGBANK(sym->type)) && - (!FUNC_ISISR(sym->type))) { - werror(E_PREV_DEF_CONFLICT, csym->name, "using"); - } + (!FUNC_ISISR(sym->type))) + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "using", csym->fileDef, csym->lineDef); if (IFFUNC_ISNAKED(csym->type) != IFFUNC_ISNAKED(sym->type)) { // disabled since __naked has no influence on the calling convention - // werror (E_PREV_DEF_CONFLICT, csym->name, "__naked"); + // werror (E_PREV_DECL_CONFLICT, csym->name, "__naked", csym->fileDef, + // csym->lineDef); FUNC_ISNAKED(sym->type) = 1; } if (FUNC_BANKED(csym->type) || FUNC_BANKED(sym->type)) { if (FUNC_NONBANKED(csym->type) || FUNC_NONBANKED(sym->type)) { - werror(W_BANKED_WITH_NONBANKED); + werrorfl(sym->fileDef, sym->lineDef, W_BANKED_WITH_NONBANKED); FUNC_BANKED(sym->type) = 0; FUNC_NONBANKED(sym->type) = 1; } else { @@ -2635,19 +2806,20 @@ int checkFunction(symbol *sym, symbol *csym) { /* Really, reentrant should match regardless of argCnt, but */ /* this breaks some existing code (the fp lib functions). If */ /* the first argument is always passed the same way, this */ - /* lax checking is ok (but may not be true for in future ports) */ + /* lax checking is ok (but may not be true for in future backends) */ if (IFFUNC_ISREENT(csym->type) != IFFUNC_ISREENT(sym->type) && argCnt > 1) { // printf("argCnt = %d\n",argCnt); - werror(E_PREV_DEF_CONFLICT, csym->name, "reentrant"); + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "reentrant", csym->fileDef, csym->lineDef); } - if (IFFUNC_ISWPARAM(csym->type) != IFFUNC_ISWPARAM(sym->type)) { - werror(E_PREV_DEF_CONFLICT, csym->name, "wparam"); - } + if (IFFUNC_ISWPARAM(csym->type) != IFFUNC_ISWPARAM(sym->type)) + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "wparam", csym->fileDef, csym->lineDef); - if (IFFUNC_ISSHADOWREGS(csym->type) != IFFUNC_ISSHADOWREGS(sym->type)) { - werror(E_PREV_DEF_CONFLICT, csym->name, "shadowregs"); - } + if (IFFUNC_ISSHADOWREGS(csym->type) != IFFUNC_ISSHADOWREGS(sym->type)) + werrorfl(sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, + "shadowregs", csym->fileDef, csym->lineDef); /* compare expected args with actual args */ exargs = FUNC_ARGS(csym->type); @@ -2691,8 +2863,16 @@ int checkFunction(symbol *sym, symbol *csym) { deleteFromSeg(csym); addSym(SymbolTab, sym, sym->name, sym->level, sym->block, 1); if (IS_EXTERN(csym->etype) && !IS_EXTERN(sym->etype)) { + SPEC_EXTR(sym->etype) = 1; addSet(&publics, sym); } + + SPEC_STAT(sym->etype) |= SPEC_STAT(csym->etype); + if (SPEC_STAT(sym->etype) && SPEC_EXTR(sym->etype)) { + werrorfl(sym->fileDef, sym->lineDef, E_TWO_OR_MORE_STORAGE_CLASSES, + sym->name); + } + return 1; } @@ -2743,7 +2923,7 @@ void processFuncArgs(symbol *func) { } /* reset regparm for the port */ - (*port->reset_regparms)(); + (*port->reset_regparms)(funcType); /* if any of the arguments is an aggregate */ /* change it to pointer to the same type */ @@ -2751,6 +2931,12 @@ void processFuncArgs(symbol *func) { int argreg = 0; struct dbuf_s dbuf; + if (val->sym && val->sym->name) + for (value *val2 = val->next; val2; val2 = val2->next) + if (val2->sym && val2->sym->name && + !strcmp(val->sym->name, val2->sym->name)) + werror(E_DUPLICATE_PARAMTER_NAME, val->sym->name, func->name); + dbuf_init(&dbuf, 128); dbuf_printf(&dbuf, "%s parameter %d", func->name, pNum); checkTypeSanity(val->etype, dbuf_c_str(&dbuf)); @@ -2790,7 +2976,7 @@ void processFuncArgs(symbol *func) { return; } } else { - /* if this function is reentrant or */ + /* if this function is reentra[nt or */ /* automatics r 2b stacked then nothing */ if (IFFUNC_ISREENT(funcType) || options.stackAuto) return; @@ -2834,7 +3020,7 @@ void processFuncArgs(symbol *func) { /*-----------------------------------------------------------------*/ /* isSymbolEqual - compares two symbols return 1 if they match */ /*-----------------------------------------------------------------*/ -int isSymbolEqual(symbol *dest, symbol *src) { +int isSymbolEqual(const symbol *dest, const symbol *src) { /* if pointers match then equal */ if (dest == src) return 1; @@ -2909,13 +3095,28 @@ void dbuf_printTypeChain(sym_link *start, struct dbuf_s *dbuf) { dbuf_append_str(dbuf, ", "); } dbuf_append_str(dbuf, ")"); - if (IFFUNC_ISREENT(type)) + if (IFFUNC_ISREENT(type) && isTargetKeyword("__reentrant")) dbuf_append_str(dbuf, " __reentrant"); if (FUNC_REGBANK(type)) { dbuf_set_length(&dbuf2, 0); dbuf_printf(&dbuf2, " __using(%d)", FUNC_REGBANK(type)); dbuf_append_str(dbuf, dbuf_c_str(&dbuf2)); } + if (IFFUNC_ISBANKEDCALL(type)) + dbuf_append_str(dbuf, " __banked"); + if (IFFUNC_ISZ88DK_CALLEE(type)) + dbuf_append_str(dbuf, " __z88dk_callee"); + if (IFFUNC_ISZ88DK_FASTCALL(type)) + dbuf_append_str(dbuf, " __z88dk_fastcall"); + for (unsigned char i = 0; i < 9; i++) + if (type->funcAttrs.preserved_regs[i]) { + dbuf_append_str(dbuf, " __preserves_regs("); + for (; i < 9; i++) + if (type->funcAttrs.preserved_regs[i]) + dbuf_printf(dbuf, " %d", i); + dbuf_append_str(dbuf, " )"); + break; + } break; case GPOINTER: dbuf_append_str(dbuf, "generic*"); @@ -2970,6 +3171,8 @@ void dbuf_printTypeChain(sym_link *start, struct dbuf_s *dbuf) { dbuf_append_str(dbuf, "const-"); if (SPEC_USIGN(type)) dbuf_append_str(dbuf, "unsigned-"); + else if (SPEC_NOUN(type) == V_CHAR) + dbuf_append_str(dbuf, "signed-"); switch (SPEC_NOUN(type)) { case V_INT: if (IS_LONGLONG(type)) @@ -3320,9 +3523,6 @@ symbol *fsdiv; symbol *fseq; symbol *fsneq; symbol *fslt; -symbol *fslteq; -symbol *fsgt; -symbol *fsgteq; symbol *fps16x16_add; symbol *fps16x16_sub; @@ -3337,6 +3537,7 @@ symbol *fps16x16_gteq; /* Dims: mul/div/mod, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED/BOTH */ symbol *muldiv[3][4][4]; +symbol *muls16tos32[2]; /* Dims: BYTE/WORD/DWORD/QWORD SIGNED/UNSIGNED */ sym_link *multypes[4][2]; /* Dims: to/from float, BYTE/WORD/DWORD/QWORD, SIGNED/USIGNED */ @@ -3350,6 +3551,8 @@ sym_link *charType; sym_link *floatType; sym_link *fixed16x16Type; +symbol *memcpy_builtin; + static const char *_mangleFunctionName(const char *in) { if (port->getMangledFunctionName) { return port->getMangledFunctionName(in); @@ -3370,7 +3573,8 @@ static const char *_mangleFunctionName(const char *in) { /* 'q' - fixed16x16 */ /* 'v' - void */ /* '*' - pointer - default (GPOINTER) */ -/* modifiers - 'u' - unsigned */ +/* modifiers - 'S' - signed */ +/* 'U' - unsigned */ /* 'C' - const */ /* pointer modifiers - 'g' - generic */ /* 'x' - xdata */ @@ -3379,23 +3583,26 @@ static const char *_mangleFunctionName(const char *in) { /* 'F' - function */ /* examples : "ig*" - generic int * */ /* "cx*" - char xdata * */ -/* "ui" - unsigned int */ +/* "Ui" - unsigned int */ +/* "Sc" - signed char */ /*-----------------------------------------------------------------*/ sym_link *typeFromStr(const char *s) { sym_link *r = newLink(DECLARATOR); + int sign = 0; int usign = 0; int constant = 0; do { sym_link *nr; switch (*s) { - case 'C': - constant = 1; + case 'S': + sign = 1; break; - case 'u': + case 'U': usign = 1; - s++; - continue; + break; + case 'C': + constant = 1; break; case 'b': r->xclass = SPECIFIER; @@ -3404,6 +3611,13 @@ sym_link *typeFromStr(const char *s) { case 'c': r->xclass = SPECIFIER; SPEC_NOUN(r) = V_CHAR; + if (!sign && !usign) + r->select.s.b_implicit_sign = true; + if (usign) { + SPEC_USIGN(r) = 1; + usign = 0; + } else if (!sign && !options.signed_char) + SPEC_USIGN(r) = 1; break; case 's': case 'i': @@ -3469,8 +3683,12 @@ sym_link *typeFromStr(const char *s) { break; default: werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "typeFromStr: unknown type"); + fprintf(stderr, "unknown: %s\n", s); break; } + if (usign && sign) + werror(E_INTERNAL_ERROR, __FILE__, __LINE__, + "typeFromStr: both signed and unsigned specified"); if (IS_SPEC(r) && usign) { SPEC_USIGN(r) = 1; usign = 0; @@ -3498,7 +3716,7 @@ void initCSupport(void) { const char *ssu[] = {"s", "su", "us", "u"}; const char *srlrr[] = {"rl", "rr"}; /* type as character codes for typeFromStr() */ - const char *sbwdCodes[] = {"c", "i", "l", "L", "uc", "ui", "ul", "uL"}; + const char *sbwdCodes[] = {"c", "i", "l", "L", "Uc", "Ui", "Ul", "UL"}; int bwd, su, muldivmod, tofrom, slsr; @@ -3527,12 +3745,13 @@ void initCSupport(void) { } multypes[bwd][0] = l; multypes[bwd][1] = copyLinkChain(l); + SPEC_USIGN(multypes[bwd][0]) = 0; SPEC_USIGN(multypes[bwd][1]) = 1; } floatType = newFloatLink(); fixed16x16Type = newFixed16x16Link(); - charType = (options.unsigned_char) ? UCHARTYPE : SCHARTYPE; + charType = (options.signed_char) ? SCHARTYPE : UCHARTYPE; fsadd = funcOfType("__fsadd", floatType, floatType, 2, options.float_rent); fssub = funcOfType("__fssub", floatType, floatType, 2, options.float_rent); @@ -3541,9 +3760,6 @@ void initCSupport(void) { fseq = funcOfType("__fseq", charType, floatType, 2, options.float_rent); fsneq = funcOfType("__fsneq", charType, floatType, 2, options.float_rent); fslt = funcOfType("__fslt", charType, floatType, 2, options.float_rent); - fslteq = funcOfType("__fslteq", charType, floatType, 2, options.float_rent); - fsgt = funcOfType("__fsgt", charType, floatType, 2, options.float_rent); - fsgteq = funcOfType("__fsgteq", charType, floatType, 2, options.float_rent); fps16x16_add = funcOfType("__fps16x16_add", fixed16x16Type, fixed16x16Type, 2, options.float_rent); @@ -3645,7 +3861,7 @@ void initCSupport(void) { /* byte */ - /* PIC16 port wants __divschar/__modschar to return an int, so that both + /* _divschar/_modschar return int, so that both * 100 / -4 = -25 and -128 / -1 = 128 can be handled correctly * (first one would have to be sign extended, second one must not be). * Similarly, modschar should be handled, but the iCode introduces cast @@ -3662,9 +3878,10 @@ void initCSupport(void) { dbuf_init(&dbuf, 128); dbuf_printf(&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su], sbwd[bwd]); - muldiv[muldivmod][bwd][su] = funcOfType( - _mangleFunctionName(dbuf_c_str(&dbuf)), multypes[bwd][su % 2], - multypes[bwd][su / 2], 2, options.intlong_rent); + muldiv[muldivmod][bwd][su] = + funcOfType(_mangleFunctionName(dbuf_c_str(&dbuf)), + multypes[bwd == 0 ? 1 : 0][su % 2], multypes[bwd][su / 2], + 2, options.intlong_rent); dbuf_destroy(&dbuf); } } @@ -3677,9 +3894,10 @@ void initCSupport(void) { dbuf_init(&dbuf, 128); dbuf_printf(&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su * 3], sbwd[bwd]); - muldiv[muldivmod][bwd][su] = funcOfType( - _mangleFunctionName(dbuf_c_str(&dbuf)), multypes[bwd][su], - multypes[bwd][su], 2, options.intlong_rent); + muldiv[muldivmod][bwd][su] = + funcOfType(_mangleFunctionName(dbuf_c_str(&dbuf)), + multypes[(bwd == 0) ? 1 : bwd][su], multypes[bwd][su], 2, + options.intlong_rent); dbuf_destroy(&dbuf); } } @@ -3725,6 +3943,17 @@ void initCSupport(void) { } } } + + { + const char *iparams[] = {"i", "i"}; + const char *uiparams[] = {"Ui", "Ui"}; + muls16tos32[0] = port->support.has_mulint2long + ? funcOfTypeVarg("__mulsint2slong", "l", 2, iparams) + : 0; + muls16tos32[1] = port->support.has_mulint2long + ? funcOfTypeVarg("__muluint2ulong", "Ul", 2, uiparams) + : 0; + } } /*-----------------------------------------------------------------*/ @@ -3734,16 +3963,26 @@ void initBuiltIns() { int i; symbol *sym; - if (!port->builtintable) - return; + if (port->builtintable) { + for (i = 0; port->builtintable[i].name; i++) { + sym = funcOfTypeVarg(port->builtintable[i].name, + port->builtintable[i].rtype, + port->builtintable[i].nParms, + (const char **)port->builtintable[i].parm_types); + FUNC_ISBUILTIN(sym->type) = 1; + FUNC_ISREENT(sym->type) = 0; /* can never be reentrant */ + } + } - for (i = 0; port->builtintable[i].name; i++) { - sym = - funcOfTypeVarg(port->builtintable[i].name, port->builtintable[i].rtype, - port->builtintable[i].nParms, - (const char **)port->builtintable[i].parm_types); - FUNC_ISBUILTIN(sym->type) = 1; - FUNC_ISREENT(sym->type) = 0; /* can never be reentrant */ + /* initialize memcpy symbol for struct assignment */ + memcpy_builtin = findSym(SymbolTab, NULL, "__builtin_memcpy"); + /* if there is no __builtin_memcpy, use __memcpy instead of an actual builtin + */ + if (!memcpy_builtin) { + const char *argTypeStrs[] = {"vg*", "Cvg*", "Ui"}; + memcpy_builtin = funcOfTypeVarg("__memcpy", "vg*", 3, argTypeStrs); + FUNC_ISBUILTIN(memcpy_builtin->type) = 0; + FUNC_ISREENT(memcpy_builtin->type) = options.stackAuto; } } @@ -3793,6 +4032,7 @@ sym_link *newEnumType(symbol *enumlist) { SPEC_USIGN(type) = 1; } else if (min >= -128 && max <= 127) { SPEC_NOUN(type) = V_CHAR; + SPEC_SIGN(type) = 1; } else if (min >= 0 && max <= 65535) { SPEC_NOUN(type) = V_INT; SPEC_USIGN(type) = 1; diff --git a/src/SDCCsymt.h b/src/SDCCsymt.h index 3b0592406..647bbd477 100644 --- a/src/SDCCsymt.h +++ b/src/SDCCsymt.h @@ -22,13 +22,16 @@ #define SDCCSYMT_H #define MAX_NEST_LEVEL 256 -#define SDCC_SYMNAME_MAX 64 +#define SDCC_SYMNAME_MAX 256 #define SDCC_NAME_MAX 3 * SDCC_SYMNAME_MAX // big enough for ___etc #include "SDCCglobl.h" #include "SDCChasht.h" -#include "dbuf.h" +#include "util/dbuf.h" -#define INTNO_MAX 255 /* maximum allowed interrupt number */ +typedef struct value value; + +#define INTNO_MAX 255 /* maximum allowed interrupt number */ +#define INTNO_TRAP INTNO_MAX #define INTNO_UNSPEC (INTNO_MAX + 1) /* interrupt number unspecified */ #define BITVAR_PAD -1 @@ -78,7 +81,7 @@ enum { typedef struct bucket { void *sym; /* pointer to the object */ char name[SDCC_NAME_MAX + 1]; /* name of this symbol */ - int level; /* nest level for this symbol */ + long level; /* nest level for this symbol */ int block; /* belongs to which block */ struct bucket *prev; /* ptr 2 previous bucket */ struct bucket *next; /* ptr 2 next bucket */ @@ -86,7 +89,7 @@ typedef struct bucket { typedef struct structdef { char tag[SDCC_NAME_MAX + 1]; /* tag part of structure */ - unsigned char level; /* Nesting level */ + long level; /* Nesting level */ int block; /* belongs to which block */ struct symbol *fields; /* pointer to fields */ unsigned size; /* sizeof the table in bytes */ @@ -143,36 +146,43 @@ typedef enum { /* specifier is the last in the type-chain */ typedef struct specifier { - NOUN noun; /* CHAR INT STRUCTURE LABEL */ - STORAGE_CLASS sclass; /* REGISTER,AUTO,FIX,CONSTANT */ - struct memmap *oclass; /* output storage class */ - unsigned b_long : 1; /* 1=long */ - unsigned b_longlong : 1; /* 1=long long */ - unsigned b_short : 1; /* 1=short int */ - unsigned b_unsigned : 1; /* 1=unsigned, 0=signed */ - unsigned b_signed : 1; /* just for sanity checks only*/ - unsigned b_static : 1; /* 1=static keyword found */ - unsigned b_extern : 1; /* 1=extern found */ - unsigned b_inline : 1; /* inline function requested */ - unsigned b_noreturn : 1; /* promised not to return */ - unsigned b_alignas : 1; /* alignment */ - unsigned b_absadr : 1; /* absolute address specfied */ - unsigned b_volatile : 1; /* is marked as volatile */ - unsigned b_const : 1; /* is a constant */ - unsigned b_restrict : 1; /* is restricted */ - struct symbol *addrspace; /* is in named address space */ - unsigned b_typedef : 1; /* is typedefed */ - unsigned b_isregparm : 1; /* is the first parameter */ - unsigned b_isenum : 1; /* is an enumerated type */ - unsigned b_bitUnnamed : 1; /* is an unnamed bit-field */ - unsigned _bitStart; /* bit start position */ - unsigned _bitLength; /* bit length */ - unsigned _addr; /* address of symbol */ - unsigned _stack; /* stack offset for stacked v */ - int argreg; /* reg no for regparm */ - union { /* Values if constant or enum */ - TYPE_TARGET_INT v_int; /* 2 bytes: int and char values */ - const char *v_char; /* character string */ + NOUN noun; /* CHAR INT STRUCTURE LABEL */ + STORAGE_CLASS sclass; /* REGISTER,AUTO,FIX,CONSTANT */ + struct memmap *oclass; /* output storage class */ + unsigned b_long : 1; /* 1=long */ + unsigned b_longlong : 1; /* 1=long long */ + unsigned b_short : 1; /* 1=short int */ + unsigned b_unsigned : 1; /* 1=unsigned, 0=signed */ + unsigned b_signed : 1; /* just for sanity checks only*/ + bool b_implicit_sign : 1; /* signedness not explicitly specified - needed to + keep char a separate type from signed char and + unsigned char. */ + unsigned b_static : 1; /* 1=static keyword found */ + unsigned b_extern : 1; /* 1=extern found */ + unsigned b_inline : 1; /* inline function requested */ + unsigned b_noreturn : 1; /* promised not to return */ + unsigned b_alignas : 1; /* alignment */ + unsigned b_absadr : 1; /* absolute address specfied */ + unsigned b_volatile : 1; /* is marked as volatile */ + unsigned b_const : 1; /* is a constant */ + unsigned b_restrict : 1; /* is restricted */ + struct symbol *addrspace; /* is in named address space */ + unsigned b_typedef : 1; /* is typedefed */ + unsigned b_isregparm : 1; /* is the first parameter */ + unsigned b_isenum : 1; /* is an enumerated type */ + unsigned b_bitUnnamed : 1; /* is an unnamed bit-field */ + unsigned b_needspar : 1; /* has to be a parameter */ + unsigned _bitStart; /* bit start position */ + unsigned _bitLength; /* bit length */ + unsigned _addr; /* address of symbol */ + unsigned _stack; /* stack offset for stacked v */ + int argreg; /* reg no for regparm */ + union { /* Values if constant or enum */ + TYPE_TARGET_INT v_int; /* 2 bytes: int and char values */ + const char *v_char; /* char character string */ + const TYPE_TARGET_UINT *v_char16; /* char16_t character string */ + const TYPE_TARGET_ULONG + *v_char32; /* char32_t character string */ TYPE_TARGET_UINT v_uint; /* 2 bytes: unsigned int const value */ TYPE_TARGET_LONG v_long; /* 4 bytes: long constant value */ TYPE_TARGET_ULONG v_ulong; /* 4 bytes: unsigned long constant value */ @@ -247,12 +257,27 @@ typedef struct sym_link { unsigned inlinereq : 1; /* inlining requested */ unsigned noreturn : 1; /* promised not to return */ unsigned smallc : 1; /* Parameters on stack are passed in reverse order */ - unsigned intno; /* 1=Interrupt service routine */ + unsigned z88dk_fastcall : 1; /* For the z80-related ports: Function has a + single paramter of at most 32 bits that is + passed in dehl */ + unsigned z88dk_callee : 1; /* Stack pointer adjustment for parameters passed + on the stack is done by the callee */ + unsigned z88dk_shortcall : 1; /* Short call available via rst (see values + later) (Z80 only) */ + unsigned + z88dk_has_params_offset : 1; /* Has a parameter offset (Z80 only) */ + unsigned intno; /* Number of interrupt for interrupt service routine */ short regbank; /* register bank 2b used */ unsigned builtin; /* is a builtin function */ unsigned javaNative; /* is a JavaNative Function (TININative ONLY) */ unsigned overlay; /* force parameters & locals into overlay segment */ unsigned hasStackParms; /* function has parameters on stack */ + bool preserved_regs[9]; /* Registers preserved by the function - may be an + underestimate */ + unsigned char z88dk_shortcall_rst; /* Rst for a short call */ + unsigned short z88dk_shortcall_val; /* Value for a short call */ + unsigned short + z88dk_params_offset; /* Additional offset from for arguments */ } funcAttrs; struct sym_link *next; /* next element on the chain */ @@ -262,8 +287,8 @@ typedef struct symbol { char name[SDCC_SYMNAME_MAX + 1]; /* Input Variable Name */ char rname[SDCC_NAME_MAX + 1]; /* internal name */ - short level; /* declaration lev,fld offset */ - short block; /* sequential block # of definition */ + long level; /* declaration lev,fld offset */ + int block; /* sequential block # of definition */ int seqPoint; /* sequence point defined or, if unbound, used */ int key; unsigned flexArrayLength; /* if the symbol specifies a struct @@ -293,37 +318,40 @@ typedef struct symbol { /* following flags are used by the backend for code generation and can be changed if a better scheme for backend is thought of */ - unsigned isLiveFcall : 1; /* is live at or across a function call */ - unsigned isspilt : 1; /* has to be spilt */ - unsigned spillA : 1; /* spilt be register allocator */ - unsigned remat : 1; /* can be remateriazed */ - unsigned isptr : 1; /* is a pointer */ - unsigned uptr : 1; /* used as a pointer */ - unsigned isFree : 1; /* used by register allocator */ - unsigned islocal : 1; /* is a local variable */ - unsigned blockSpil : 1; /* spilt at block level */ - unsigned remainSpil : 1; /* spilt because not used in remainder */ - unsigned stackSpil : 1; /* has been spilt on temp stack location */ - unsigned onStack : 1; /* this symbol allocated on the stack */ - unsigned iaccess : 1; /* indirect access */ - unsigned ruonly : 1; /* used in return statement only */ - unsigned spildir : 1; /* spilt in direct space */ - unsigned ptrreg : 1; /* this symbol assigned to a ptr reg */ - unsigned noSpilLoc : 1; /* cannot be assigned a spil location */ - unsigned isstrlit; /* is a string literal and it's usage count */ - unsigned accuse; /* can be left in the accumulator - On the Z80 accuse is divided into - ACCUSE_A and ACCUSE_HL as the idea - is quite similar. - */ - unsigned dptr; /* 8051 variants with multiple DPTRS - currently implemented in DS390 only - */ - int allocreq; /* allocation is required for this variable */ - int stack; /* offset on stack */ - int xstack; /* offset on xternal stack */ - short nRegs; /* number of registers required */ - short regType; /* type of register required */ + unsigned isLiveFcall : 1; /* is live at or across a function call */ + unsigned isspilt : 1; /* has to be spilt */ + unsigned spillA : 1; /* spilt be register allocator */ + unsigned remat : 1; /* can be remateriazed */ + unsigned isptr : 1; /* is a pointer */ + unsigned uptr : 1; /* used as a pointer */ + unsigned isFree : 1; /* used by register allocator */ + unsigned islocal : 1; /* is a local variable */ + unsigned blockSpil : 1; /* spilt at block level */ + unsigned remainSpil : 1; /* spilt because not used in remainder */ + unsigned stackSpil : 1; /* has been spilt on temp stack location */ + unsigned onStack : 1; /* this symbol allocated on the stack */ + unsigned iaccess : 1; /* indirect access */ + unsigned ruonly : 1; /* used in return statement only */ + unsigned spildir : 1; /* spilt in direct space */ + unsigned ptrreg : 1; /* this symbol assigned to a ptr reg */ + unsigned noSpilLoc : 1; /* cannot be assigned a spil location */ + bool funcDivFlagSafe : 1; /* we know this function is safe to call with + undocumented stm8 flag bit 6 set*/ + bool funcUsesVolatile : 1; /* The function accesses a volatile variable */ + unsigned isstrlit; /* is a string literal and it's usage count */ + unsigned accuse; /* can be left in the accumulator + On the Z80 accuse is divided into + ACCUSE_A and ACCUSE_HL as the idea + is quite similar. + */ + unsigned dptr; /* 8051 variants with multiple DPTRS + currently implemented in DS390 only + */ + int allocreq; /* allocation is required for this variable */ + int stack; /* offset on stack */ + int xstack; /* offset on xternal stack */ + short nRegs; /* number of registers required */ + short regType; /* type of register required */ struct reg_info *regs[8]; /* can have at the most 8 registers */ struct asmop *aop; /* asmoperand for this symbol */ @@ -336,10 +364,10 @@ typedef struct symbol { struct symbol *spillLoc; /* register spil location */ struct set *itmpStack; /* symbols spilt @ this stack location */ } usl; - signed char bitVar; /* if bitVar != 0: this is a bit variable, bitVar is the - size in bits */ - char bitUnnamed : 1; /* unnamed bit variable */ - unsigned offset; /* offset from top if struct */ + int bitVar; /* if bitVar != 0: this is a bit variable, bitVar is the size in + bits */ + unsigned bitUnnamed : 1; /* unnamed bit variable */ + unsigned offset; /* offset from top if struct */ int lineDef; /* defined line number */ char *fileDef; /* defined filename */ @@ -435,6 +463,16 @@ extern sym_link *validateLink(sym_link *l, const char *macro, const char *args, #define IFFUNC_ISOVERLAY(x) (IS_FUNC(x) && FUNC_ISOVERLAY(x)) #define FUNC_ISSMALLC(x) (x->funcAttrs.smallc) #define IFFUNC_ISSMALLC(x) (IS_FUNC(x) && FUNC_ISSMALLC(x)) +#define FUNC_ISZ88DK_FASTCALL(x) (x->funcAttrs.z88dk_fastcall) +#define IFFUNC_ISZ88DK_FASTCALL(x) (IS_FUNC(x) && FUNC_ISZ88DK_FASTCALL(x)) +#define FUNC_ISZ88DK_CALLEE(x) (x->funcAttrs.z88dk_callee) +#define IFFUNC_ISZ88DK_CALLEE(x) (IS_FUNC(x) && FUNC_ISZ88DK_CALLEE(x)) +#define FUNC_ISZ88DK_SHORTCALL(x) (x->funcAttrs.z88dk_shortcall) +#define IFFUNC_ISZ88DK_SHORTCALL(x) (IS_FUNC(x) && FUNC_ISZ88DK_SHORTCALL(x)) + +#define BANKED_FUNCTIONS false +#define IFFUNC_ISBANKEDCALL(x) \ + (IS_FUNC(x) && (FUNC_BANKED(x) || (BANKED_FUNCTIONS && !FUNC_NONBANKED(x)))) #define SPEC_NOUN(x) \ validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.noun @@ -450,6 +488,9 @@ extern sym_link *validateLink(sym_link *l, const char *macro, const char *args, #define SPEC_USIGN(x) \ validateLink(x, "SPEC_USIGN", #x, SPECIFIER, __FILE__, __LINE__) \ ->select.s.b_unsigned +#define SPEC_SIGN(x) \ + validateLink(x, "SPEC_SIGN", #x, SPECIFIER, __FILE__, __LINE__) \ + ->select.s.b_signed #define SPEC_SCLS(x) \ validateLink(x, "SPEC_SCLS", #x, SPECIFIER, __FILE__, __LINE__) \ ->select.s.sclass @@ -490,8 +531,11 @@ extern sym_link *validateLink(sym_link *l, const char *macro, const char *args, validateLink(x, "SPEC_BLEN", #x, SPECIFIER, __FILE__, __LINE__) \ ->select.s._bitLength #define SPEC_BUNNAMED(x) \ - validateLink(x, "SPEC_BLEN", #x, SPECIFIER, __FILE__, __LINE__) \ + validateLink(x, "SPEC_BUNNAMED", #x, SPECIFIER, __FILE__, __LINE__) \ ->select.s.b_bitUnnamed +#define SPEC_NEEDSPAR(x) \ + validateLink(x, "SPEC_NEEDSPAR", #x, SPECIFIER, __FILE__, __LINE__) \ + ->select.s.b_needspar /* Sleaze: SPEC_ISR_SAVED_BANKS is only used on * function type symbols, which obviously cannot @@ -625,9 +669,6 @@ extern symbol *fsdiv; extern symbol *fseq; extern symbol *fsneq; extern symbol *fslt; -extern symbol *fslteq; -extern symbol *fsgt; -extern symbol *fsgteq; extern symbol *fps16x16_add; extern symbol *fps16x16_sub; @@ -642,6 +683,8 @@ extern symbol *fps16x16_gteq; /* Dims: mul/div/mod, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED/BOTH */ extern symbol *muldiv[3][4][4]; +/* 16 x 16 -> 32 multiplication SIGNED/UNSIGNED */ +extern symbol *muls16tos32[2]; /* Dims: BYTE/WORD/DWORD/QWORD SIGNED/UNSIGNED */ extern sym_link *multypes[4][2]; /* Dims: to/from float, BYTE/WORD/DWORD/QWORD, SIGNED/USIGNED */ @@ -651,6 +694,8 @@ extern symbol *fp16x16conv[2][5][2]; /* Dims: shift left/shift right, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED */ extern symbol *rlrr[2][4][2]; +extern symbol *memcpy_builtin; + #define SCHARTYPE multypes[0][0] #define UCHARTYPE multypes[0][1] #define INTTYPE multypes[1][0] @@ -677,7 +722,7 @@ typedef enum { /* forward definitions for the symbol table related functions */ void initSymt(); -symbol *newSymbol(const char *, int); +symbol *newSymbol(const char *, long); sym_link *newLink(SYM_LINK_CLASS); sym_link *newFloatLink(); structdef *newStruct(const char *); @@ -687,14 +732,12 @@ sym_link *mergeSpec(sym_link *, sym_link *, const char *name); sym_link *mergeDeclSpec(sym_link *, sym_link *, const char *name); symbol *reverseSyms(symbol *); sym_link *reverseLink(sym_link *); -symbol *copySymbol(symbol *); -symbol *copySymbolChain(symbol *); -void printSymChain(symbol *, int); -void printStruct(structdef *, int); -char *genSymName(int); +symbol *copySymbol(const symbol *); +symbol *copySymbolChain(const symbol *); +char *genSymName(long); sym_link *getSpec(sym_link *); int compStructSize(int, structdef *); -sym_link *copyLinkChain(sym_link *); +sym_link *copyLinkChain(const sym_link *); int checkDecl(symbol *, int); void checkBasic(sym_link *, sym_link *); value *checkPointerIval(sym_link *, value *); @@ -709,10 +752,10 @@ sym_link *newLongLink(); sym_link *newBoolLink(); sym_link *newVoidLink(); int compareType(sym_link *, sym_link *); -int compareTypeExact(sym_link *, sym_link *, int); +int compareTypeExact(sym_link *, sym_link *, long); int compareTypeInexact(sym_link *, sym_link *); int checkFunction(symbol *, symbol *); -void cleanUpLevel(bucket **, int); +void cleanUpLevel(bucket **, long); void cleanUpBlock(bucket **, int); symbol *getAddrspace(sym_link *type); int funcInChain(sym_link *); @@ -722,7 +765,7 @@ symbol *getStructElement(structdef *, symbol *); sym_link *computeType(sym_link *, sym_link *, RESULT_TYPE, int); void processFuncPtrArgs(sym_link *); void processFuncArgs(symbol *); -int isSymbolEqual(symbol *, symbol *); +int isSymbolEqual(const symbol *, const symbol *); int powof2(TYPE_TARGET_ULONG); void dbuf_printTypeChain(sym_link *, struct dbuf_s *); void printTypeChain(sym_link *, FILE *); @@ -733,11 +776,11 @@ void pointerTypes(sym_link *, sym_link *); void cdbStructBlock(int); void initHashT(); bucket *newBucket(); -void addSym(bucket **, void *, char *, int, int, int checkType); +void addSym(bucket **, void *, char *, long, int, int checkType); void deleteSym(bucket **, void *, const char *); void *findSym(bucket **, void *, const char *); void *findSymWithLevel(bucket **, struct symbol *); -void *findSymWithBlock(bucket **, struct symbol *, int); +void *findSymWithBlock(bucket **, struct symbol *, int, long); void changePointer(sym_link *p); void checkTypeSanity(sym_link *etype, const char *name); sym_link *typeFromStr(const char *); diff --git a/src/SDCCtree_dec.hpp b/src/SDCCtree_dec.hpp index ca4274137..dcbb6b9c9 100644 --- a/src/SDCCtree_dec.hpp +++ b/src/SDCCtree_dec.hpp @@ -45,6 +45,7 @@ // // void thorup_elimination_ordering(l_t &l, const J_t &J) // Creates an elimination ordering l of a graph J using Thorup's heuristic. +// #include #include @@ -55,9 +56,13 @@ #include #include #include +#include #include #include +#undef RANGE +#undef BLOCK + struct forget_properties { template void operator()(const T1 &, const T2 &) const {} }; @@ -145,8 +150,8 @@ void thorup_E(std::multimap &M, const I_t &I) { s.push(std::pair(i2, j)); } - // Not in Thorup's paper, but without this the algorithm gives incorrect - // results. + // Thorup forgot this in his paper. Without it, some maximal chains are + // omitted. while (s.size() > 1) { M.insert( std::pair(s.top().second, s.top().first)); @@ -161,9 +166,9 @@ void thorup_E(std::multimap &M, const I_t &I) { // complexity of this O(|I|log|I|), could be reduced to O(|I|). template void thorup_elimination_ordering(l_t &l, const G_t &G) { - // Should we do this? Or just use G as J? The Thorup paper seems unclear, it - // speaks of statements that contain jumps to other statements, but does it - // count as a jump, when they're just subsequent? + // Remove edges to immediately following instruction. By "each statement can + // have at most obne jump" in the last paragraph of Appendix A it is clear + // that Thorup does not consider the implicit next-instruction-edges as jumps. boost::adjacency_list J; boost::copy_graph( G, J, @@ -187,11 +192,11 @@ void thorup_elimination_ordering(l_t &l, const G_t &G) { // Finds a (the newest) bag that contains all vertices in X in the tree // decomposition T. -template -typename boost::graph_traits::vertex_iterator -find_bag(const std::set &X, const T_t &T) { +template +typename boost::graph_traits::vertex_iterator find_bag(const X_t &X, + const T_t &T) { typedef typename boost::graph_traits::vertex_iterator T_vertex_iter_t; - typedef typename std::set::const_iterator vertex_index_iter_t; + typedef typename X_t::const_iterator vertex_index_iter_t; T_vertex_iter_t t, t_end, t_found; vertex_index_iter_t v; @@ -215,8 +220,8 @@ find_bag(const std::set &X, const T_t &T) { } // Add edges to make the vertices in X a clique in G. -template void make_clique(const std::set &X, G_t &G) { - std::set::const_iterator n1, n2; +template void make_clique(const X_t &X, G_t &G) { + typename X_t::const_iterator n1, n2; for (n1 = X.begin(); n1 != X.end(); n1++) for (n2 = n1, ++n2; n2 != X.end(); ++n2) add_edge(*n1, *n2, G); @@ -241,7 +246,7 @@ void add_vertices_to_tree_decomposition(T_t &T, const v_t v, const v_t v_end, // Get the neigbours adjacency_iter_t n, n_end; - std::set neighbours; + decltype(T[0].bag) neighbours; for (boost::tie(n, n_end) = boost::adjacent_vertices(*v, G); n != n_end; ++n) if (active[index[*n]]) neighbours.insert(index[*n]); @@ -262,7 +267,7 @@ void add_vertices_to_tree_decomposition(T_t &T, const v_t v, const v_t v_end, T[s].bag.insert(*v); } -// Create a tree decomposition from en elimination ordering. +// Create a tree decomposition from an elimination ordering. template void tree_decomposition_from_elimination_ordering( T_t &T, const std::list &l, const G_t &G) { @@ -384,6 +389,16 @@ void nicify_diffs(T_t &T, c0 = *c; nicify_diffs(T, c0); + // Redundant bags are isolated, and thus marked for later removal. + if (T[t].bag == T[c0].bag) { + T[c0].bag.clear(); + boost::remove_edge(t, c0, T); + adjacency_iter_t c, c_end; + for (boost::tie(c, c_end) = adjacent_vertices(c0, T); c != c_end; ++c) + boost::add_edge(t, *c, T); + boost::clear_vertex(c0, T); + } + if (std::includes(T[t].bag.begin(), T[t].bag.end(), T[c0].bag.begin(), T[c0].bag.end()) || std::includes(T[c0].bag.begin(), T[c0].bag.end(), T[t].bag.begin(), @@ -400,7 +415,7 @@ void nicify_diffs(T_t &T, boost::add_edge(t, d, T); } -// // Ensure that all nodes' bags' sizes differ by at most one to their +// Ensure that all nodes' bags' sizes differ by at most one to their // successors'. template void nicify_diffs_more(T_t &T, @@ -433,7 +448,11 @@ void nicify_diffs_more(T_t &T, c1 = *c; nicify_diffs_more(T, c0); nicify_diffs_more(T, c1); - T[t].weight = std::min(T[c0].weight, T[c1].weight) + 1; + { + const unsigned l = T[c0].weight; + const unsigned r = T[c1].weight; + T[t].weight = (l == r) ? l + 1 : std::max(l, r); + } return; default: std::cerr << "nicify_diffs_more error.\n"; @@ -456,7 +475,7 @@ void nicify_diffs_more(T_t &T, boost::add_edge(d, c0, T); boost::remove_edge(t, c0, T); T[d].bag = T[t_size > c0_size ? t : c0].bag; - std::set::iterator i; + typename decltype(T[d].bag)::iterator i; for (i = T[d].bag.begin(); T[t_size < c0_size ? t : c0].bag.find(*i) != T[t_size < c0_size ? t : c0].bag.end(); ++i) @@ -467,6 +486,15 @@ void nicify_diffs_more(T_t &T, nicify_diffs_more(T, t); } +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +#include +#include +#include +#include + +using treedec::find_root; +#else // Find a root of an acyclic graph T // Complexity: Linear in the number of vertices of T. template @@ -482,6 +510,25 @@ typename boost::graph_traits::vertex_descriptor find_root(T_t &T) { return (t); } +#endif + +// Remove isolated vertices possibly introduced by nicify_diffs(). Complicated, +// since boost does not support removing more than one vertex at a time. +template void remove_isolated_vertices(T_t &T) { + bool change = true; + while (change) { + change = false; + if (boost::num_vertices(T) <= 1) + return; + + for (unsigned int i = 0; i < boost::num_vertices(T); i++) + if (!boost::out_degree(i, T) && !boost::in_degree(i, T)) { + remove_vertex(i, T); + change = true; + break; + } + } +} // Transform a tree decomposition into a nice tree decomposition. template void nicify(T_t &T) { @@ -499,4 +546,114 @@ template void nicify(T_t &T) { nicify_joins(T, t); nicify_diffs(T, t); nicify_diffs_more(T, t); + remove_isolated_vertices(T); +} + +class cfg_titlewriter { +public: + explicit cfg_titlewriter(const std::string &f, const std::string &p) + : function(f), purpose(p) {} + void operator()(std::ostream &out) const { + out << "graph [label=\"Control-flow-graph for " << purpose << " (function " + << function << ")\"]\n"; + } + +private: + std::string function; + std::string purpose; +}; + +class dec_titlewriter { +public: + explicit dec_titlewriter(unsigned int w, const std::string &f, + const std::string &p) + : function(f), purpose(p) { + width = w; + } + void operator()(std::ostream &out) const { + out << "graph [label=\"Tree-decomposition of width " << width << " for " + << purpose << " (function " << function << ")\"]\n"; + } + +private: + unsigned int width; + std::string function; + std::string purpose; +}; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + +#include +#include +#include + +#include +#include +#include + +template +void copy_undir(Gd_t &dst, const Gs_t &src) { + for (unsigned i = 0; i < boost::num_vertices(src); i++) + boost::add_vertex(dst); + + typename boost::graph_traits::edge_iterator e, e_end; + for (boost::tie(e, e_end) = boost::edges(src); e != e_end; ++e) { + wassert(boost::source(*e, src) != boost::target(*e, src)); + if (!boost::edge(boost::source(*e, src), boost::target(*e, src), dst) + .second) + boost::add_edge(boost::source(*e, src), boost::target(*e, src), dst); + } +} + +#endif + +#undef USE_THORUP // Thorup's classic algorithm (default in SDCC pre-3.7.0). + // Substantially worse width than the others. +#define USE_PP_FI_TM 1 // A good trade-off between width and runtime +#undef USE_EX17 // Slightly better width than PP_FI_TM, but no polynomial + // runtime bound. +#undef USE_PP_MD // Slightly worse width than PP_FI_TM. +#undef USE_PP_FI // Slightly worse width than PP_FI_TM. + +// Get a nice tree decomposition for a cfg. +template +void get_nice_tree_decomposition(T_t &tree_dec, const G_t &cfg) { + thorup_tree_decomposition(tree_dec, cfg); + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + +#ifdef USE_THORUP + treedec::thorup a2(cfg); +#else + typedef boost::adjacency_list + cfg2_t; + cfg2_t cfg2; + copy_undir(cfg2, cfg); +#if USE_PP_FI_TM + treedec::comb::PP_FI_TM a2(cfg2); +#elif USE_EX17 + treedec::comb::ex17 a2(cfg2); +#elif USE_PP_MD + treedec::comb::PP_MD a2(cfg2); +#elif USE_PP_FI + treedec::comb::PP_FI a2(cfg2); +#else +#error No algorithm selected +#endif +#endif + + T_t tree_dec2; + a2.do_it(); + a2.get_tree_decomposition(tree_dec2); + wassert(treedec::is_valid_treedecomposition(cfg, tree_dec2)); + + if (treedec::get_width(tree_dec2) < treedec::get_width(tree_dec)) + std::swap(tree_dec, tree_dec2); +#endif + + nicify(tree_dec); + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + wassert(treedec::is_valid_treedecomposition(cfg, tree_dec)); +#endif } diff --git a/src/SDCCutil.c b/src/SDCCutil.c index 8023a67ea..97d9cb606 100644 --- a/src/SDCCutil.c +++ b/src/SDCCutil.c @@ -34,9 +34,9 @@ #include "SDCCglobl.h" #include "SDCCmacro.h" #include "SDCCutil.h" -#include "dbuf.h" -#include "dbuf_string.h" -#include "newalloc.h" +#include "util/dbuf.h" +#include "util/dbuf_string.h" +#include "util/newalloc.h" #include #include "version.h" @@ -593,19 +593,13 @@ const char *getBuildEnvironment(void) { #endif } -#if defined(HAVE_VSNPRINTF) || defined(HAVE_VSPRINTF) size_t SDCCsnprintf(char *dst, size_t n, const char *fmt, ...) { va_list args; int len; va_start(args, fmt); -#if defined(HAVE_VSNPRINTF) len = vsnprintf(dst, n, fmt, args); -#else - vsprintf(dst, fmt, args); - len = strlen(dst) + 1; -#endif va_end(args); @@ -619,7 +613,6 @@ size_t SDCCsnprintf(char *dst, size_t n, const char *fmt, ...) { return len; } -#endif /** Pragma tokenizer */ @@ -673,7 +666,7 @@ void free_pragma_token(struct pragma_token_s *token) { /param src Pointer to 'x' from start of hex character value */ -unsigned char hexEscape(const char **src) { +unsigned long int hexEscape(const char **src) { char *s; unsigned long value; @@ -684,47 +677,45 @@ unsigned char hexEscape(const char **src) { if (s == *src) { /* no valid hex found */ werror(E_INVALID_HEX); - } else { - if (value > 255) { - werror(W_ESC_SEQ_OOR_FOR_CHAR); - } } + *src = s; - return (unsigned char)value; + return value; } /*------------------------------------------------------------------*/ /* universalEscape - process an hex constant of exactly four digits */ -/* return the hex value, throw a warning for illegal octal */ +/* return the hex value, throw an error warning for invalid hex */ /* adjust src to point at the last proccesed char */ /*------------------------------------------------------------------*/ -unsigned char universalEscape(const char **str, unsigned int n) { +unsigned long int universalEscape(const char **str, unsigned int n) { unsigned int digits; - unsigned value = 0; + unsigned long value = 0; const char *s = *str; - if (!options.std_c99) { - werror(W_UNIVERSAL_C99); + if (!options.std_c95) { + werror(W_UNIVERSAL_C95); } ++*str; /* Skip over the 'u' or 'U' */ for (digits = 0; digits < n; ++digits) { - if (**str >= '0' && **str <= '7') { + if (**str >= '0' && **str <= '9') { value = (value << 4) + (**str - '0'); ++*str; - } else if ((**str | 0x20) >= 'a' && (**str | 0x20) <= 'f') { - value = (value << 4) + (**str - ('a' + 10)); + } else if (tolower((unsigned char)(**str)) >= 'a' && + (tolower((unsigned char)(**str)) <= 'f')) { + value = (value << 4) + (tolower((unsigned char)(**str)) - 'a' + 10); ++*str; } else break; } - if (digits != n) { + if (digits != n || + value < 0x00a0 && value != 0x0024 && value != 0x0040 && value != 0x0060 || + value >= 0xd800 && 0xdfff >= value) { werror(E_INVALID_UNIVERSAL, s); - } else if (value > 255) { - werror(W_ESC_SEQ_OOR_FOR_CHAR); } return value; @@ -736,7 +727,7 @@ unsigned char universalEscape(const char **str, unsigned int n) { /* adjust src to point at the last proccesed char */ /*------------------------------------------------------------------*/ -unsigned char octalEscape(const char **str) { +unsigned long int octalEscape(const char **str) { int digits; unsigned value = 0; @@ -748,11 +739,7 @@ unsigned char octalEscape(const char **str) { break; } } - if (digits) { - if (value > 255 /* || (**str>='0' && **str<='7') */) { - werror(W_ESC_SEQ_OOR_FOR_CHAR); - } - } + return value; } @@ -782,7 +769,8 @@ const char *copyStr(const char *src, size_t *size) { } ++src; } else if (*src == '\\') { - int c; + unsigned long int c; + bool universal = FALSE; if (begin) { /* copy what we have until now */ @@ -838,11 +826,13 @@ const char *copyStr(const char *src, size_t *size) { case 'u': c = universalEscape(&src, 4); + universal = TRUE; --src; break; case 'U': c = universalEscape(&src, 8); + universal = TRUE; --src; break; @@ -854,7 +844,29 @@ const char *copyStr(const char *src, size_t *size) { c = *src; break; } - dbuf_append_char(&dbuf, c); + if (universal) // Encode one utf-32 character to utf-8 + { + char s[5] = "\0\0\0\0"; + if (c < 0x80) + s[0] = (char)c; + else if (c < 0x800) { + s[0] = (c >> 6) & 0x1f | 0xc0; + s[1] = (c >> 0) & 0x3f | 0x80; + } else if (c < 0x10000) { + s[0] = (c >> 12) & 0x0f | 0xe0; + s[1] = (c >> 6) & 0x3f | 0x80; + s[2] = (c >> 0) & 0x3f | 0x80; + } else if (c < 0x110000) { + s[0] = (c >> 18) & 0x07 | 0xf0; + s[1] = (c >> 12) & 0x3f | 0x80; + s[2] = (c >> 6) & 0x3f | 0x80; + s[3] = (c >> 0) & 0x3f | 0x80; + } else + wassert(0); + dbuf_append_str(&dbuf, s); + } else + dbuf_append_char(&dbuf, (char)c); + ++src; } else { if (!begin) @@ -877,3 +889,96 @@ const char *copyStr(const char *src, size_t *size) { return dbuf_detach_c_str(&dbuf); } + +static char prefix[256] = ""; +static char suffix[256] = ""; +static char cmd[4096] = ""; + +void getPrefixSuffix(const char *arg) { + const char *p; + const char sdcc[] = "sdcc"; + + if (!arg) + return; + + p = arg + strlen(arg); + while (p != arg && *(p - 1) != '\\' && *(p - 1) != '/') + p--; + arg = p; + + /* found no "sdcc" in command line argv[0] */ + if ((p = strstr(arg, sdcc)) == NULL) + return; + + /* found more than one "sdcc" in command line argv[0] */ + if (strstr(p + strlen(sdcc), sdcc) != NULL) + return; + + /* copy prefix and suffix */ + strncpy(prefix, arg, p - arg); + strcpy(suffix, p + strlen(sdcc)); +} + +char *setPrefixSuffix(const char *arg) { + const char *p; + + if (!arg) + return NULL; + else + memset(cmd, 0x00, sizeof(cmd)); + + /* find the core name of command line */ + for (p = arg; (*p) && isblank(*p); p++) + ; + arg = p; + assert(strstr(arg, ".exe") == NULL); + for (p = arg; (*p) && !isblank(*p); p++) + ; + + /* compose new command line with prefix and suffix */ + strcpy(cmd, prefix); + strncat(cmd, arg, p - arg); + strcat(cmd, suffix); + strcat(cmd, p); + + return cmd; +} + +char *formatInlineAsm(char *asmStr) { + char *p, *q; + + if (!asmStr) + return NULL; + else + q = asmStr; + + for (;;) { + // omit leading space or tab + while (*q == '\t' || *q == ' ') + q++; + // then record the head of current line + p = q; + // search for CL or reach the end + while (*q != '\n' && *q != '\r' && *q != 0) + q++; + // omit more CL characters + while (*q == '\n' || *q == '\r') + q++; + // replace the first with tab + while (p != q) + if (*p == '\t') // '\t' appears first then no need to do + { + break; + } else if (*p == ' ') // find the first space then replace it with tab + { + *p = '\t'; + break; + } else // go on to search + { + p++; + } + // check if end + if (*q == 0) + return asmStr; + } +} diff --git a/src/SDCCutil.h b/src/SDCCutil.h index 7995d48d3..1fa468848 100644 --- a/src/SDCCutil.h +++ b/src/SDCCutil.h @@ -131,31 +131,8 @@ const char *getBuildEnvironment(void); */ size_t SDCCsnprintf(char *, size_t, const char *, ...); -#if defined(HAVE_VSNPRINTF) - -/* best option: we can define our own snprintf which logs errors. - */ #define SNPRINTF SDCCsnprintf -#elif defined(HAVE_SPRINTF) - -/* if we can't build a safe snprintf for lack of vsnprintf but there - * is a native snprintf, use it. - */ -#define SNPRINTF snprintf - -#elif defined(HAVE_VSPRINTF) - -/* we can at least define our own unsafe version. - */ -#define SNPRINTF SDCCsnprintf - -#else -/* We don't have a native snprintf nor the functions we need to write one. - */ -#error "Need at least one of snprintf, vsnprintf, vsprintf!" -#endif - /** Pragma tokenizer */ enum pragma_token_e { TOKEN_UNKNOWN, TOKEN_STR, TOKEN_INT, TOKEN_EOL }; @@ -173,9 +150,13 @@ char *get_pragma_token(const char *s, struct pragma_token_s *token); const char *get_pragma_string(struct pragma_token_s *token); void free_pragma_token(struct pragma_token_s *token); -unsigned char hexEscape(const char **src); -unsigned char universalEscape(const char **src, unsigned int n); -unsigned char octalEscape(const char **src); +unsigned long int hexEscape(const char **src); +unsigned long int universalEscape(const char **src, unsigned int n); +unsigned long int octalEscape(const char **src); const char *copyStr(const char *src, size_t *size); +void getPrefixSuffix(const char *); +char *setPrefixSuffix(const char *); + +char *formatInlineAsm(char *); #endif diff --git a/src/SDCCval.c b/src/SDCCval.c index 270cefb45..8aca07cc8 100644 --- a/src/SDCCval.c +++ b/src/SDCCval.c @@ -31,7 +31,7 @@ #include #include -int cNestLevel; +long cNestLevel; /*-----------------------------------------------------------------*/ /* newValue - allocates and returns a new value */ @@ -130,13 +130,22 @@ bool convertIListToConstList(initList *src, literalList **lList, int size) { iLoop = src ? src->init.deep : NULL; while (size--) { - double val = iLoop ? AST_FLOAT_VALUE(iLoop->init.node) : 0; + literalList ll = {0}; + value *val = iLoop ? AST_VALUE(iLoop->init.node) : NULL; + if (val) { + ll.isFloat = IS_FLOAT(val->type); + if (ll.isFloat) + ll.value.f64 = floatFromVal(val); + else + ll.value.ull = ullFromVal(val); + } - if (last && last->literalValue == val) { + if (last && ((!last->isFloat && last->value.ull == ll.value.ull) || + (last->isFloat && last->value.f64 == ll.value.f64))) { last->count++; } else { newL = Safe_alloc(sizeof(literalList)); - newL->literalValue = val; + *newL = ll; newL->count = 1; newL->next = NULL; @@ -166,7 +175,7 @@ literalList *copyLiteralList(literalList *src) { while (src) { newL = Safe_alloc(sizeof(literalList)); - newL->literalValue = src->literalValue; + *newL = *src; newL->count = src->count; newL->next = NULL; @@ -227,7 +236,7 @@ double list2int(initList *val) { /*------------------------------------------------------------------*/ /* list2val - converts the first element of the list to value */ /*------------------------------------------------------------------*/ -value *list2val(initList *val) { +value *list2val(initList *val, int check) { if (!val) return NULL; @@ -235,9 +244,12 @@ value *list2val(initList *val) { return NULL; if (val->type == INIT_DEEP) - return list2val(val->init.deep); + return list2val(val->init.deep, check); + + if (val->type == INIT_NODE && val->init.node->opval.op == CAST) + return constExprValue(val->init.node->right, check); - return constExprValue(val->init.node, TRUE); + return constExprValue(val->init.node, check); } /*------------------------------------------------------------------*/ @@ -258,7 +270,7 @@ ast *list2expr(initList *ilist) { /* resolveIvalSym - resolve symbols in initial values */ /*------------------------------------------------------------------*/ void resolveIvalSym(initList *ilist, sym_link *type) { - int is_ptr = IS_PTR(type); + int is_ptr = IS_PTR(type) || (IS_ARRAY(type) && IS_PTR(type->next)); RESULT_TYPE resultType = getResultTypeFromType(getSpec(type)); while (ilist) { @@ -433,9 +445,11 @@ initList *reorderIlist(sym_link *type, initList *ilist) { } /* okay, allocate enough space */ - if (IS_ARRAY(type)) + if (IS_ARRAY(type)) { size = getNelements(type, ilist); - else if (IS_STRUCT(type)) { + if (size == 0) + return NULL; + } else if (IS_STRUCT(type)) { /* compute size from struct type. */ size = 0; for (sflds = SPEC_STRUCT(type)->fields; sflds; sflds = sflds->next) { @@ -464,10 +478,13 @@ initList *reorderIlist(sym_link *type, initList *ilist) { iloop->designation->designator.tag); else werrorfl(iloop->filename, iloop->lineno, E_BAD_DESIGNATOR); - } else + } else { assert(0); + } if (iloop->designation->next) { + if (idx >= size) + continue; if (nlistArray[idx] == NULL) nlistArray[idx] = newiList(INIT_DEEP, NULL); moveNestedInit(nlistArray[idx], iloop); @@ -477,9 +494,10 @@ initList *reorderIlist(sym_link *type, initList *ilist) { /* overwrite any existing entry with iloop */ if (iloop->type != INIT_HOLE) { + if (idx >= size) + continue; if (nlistArray[idx] != NULL) werrorfl(iloop->filename, iloop->lineno, W_DUPLICATE_INIT, idx); - nlistArray[idx] = iloop; } } @@ -659,7 +677,9 @@ checkConstantRange(sym_link *var, sym_link *lit, int op, if (IS_BOOLEAN(var)) return CCR_OK; - if (getenv("SDCC_VERY_PEDANTIC")) { + if (1) // Though the else branch is dead, I still would like to keep it. + // if (getenv ("SDCC_VERY_PEDANTIC")) + { if (SPEC_USIGN(var)) { if ((!litValUnsigned && litVal < 0) || (litVal & signExtMask) != 0) return CCR_OVL; @@ -986,38 +1006,67 @@ value *constFixed16x16Val(const char *s) { } /*-----------------------------------------------------------------*/ -/* constVal - converts an INTEGER constant into a cheapest value */ +/* constVal - converts a constant into a cheap value type */ /*-----------------------------------------------------------------*/ value *constVal(const char *s) { - value *val; + value *val = constIntVal(s); + + wassert(SPEC_NOUN(val->type) == V_INT); + + if (SPEC_LONGLONG(val->type)) + ; + else if (SPEC_LONG(val->type)) + ; + else if (SPEC_USIGN(val->type)) { + unsigned int i = SPEC_CVAL(val->type).v_uint; + if (i < 256) + SPEC_NOUN(val->type) = V_CHAR; + } else { + int i = SPEC_CVAL(val->type).v_int; + if (i >= 0 && i < 256) { + SPEC_NOUN(val->type) = V_CHAR; + SPEC_USIGN(val->type) = TRUE; + SPEC_CVAL(val->type).v_uint = i; + } else if (i >= -128 && i < 128) { + SPEC_NOUN(val->type) = V_CHAR; + } + } + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constIntVal - converts an integer constant into correct type */ +/* See ISO C11, section 6.4.4.1 for the rules. */ +/*-----------------------------------------------------------------*/ +value *constIntVal(const char *s) { char *p, *p2; double dval; long long int llval; - bool is_integral = 0; - - val = newValue(); /* alloc space for value */ + value *val = newValue(); + bool decimal, u_suffix = FALSE, l_suffix = FALSE, ll_suffix = FALSE; - val->type = val->etype = newLink(SPECIFIER); /* create the specifier */ + val->type = val->etype = newLink(SPECIFIER); SPEC_SCLS(val->type) = S_LITERAL; SPEC_CONST(val->type) = 1; - /* let's start with a signed char */ - SPEC_NOUN(val->type) = V_CHAR; SPEC_USIGN(val->type) = 0; errno = 0; + if (s[0] == '0') { if (s[1] == 'b' || s[1] == 'B') llval = strtoull(s + 2, &p, 2); else llval = strtoull(s, &p, 0); dval = (double)(unsigned long long int)llval; - is_integral = 1; + decimal = FALSE; } else { dval = strtod(s, &p); if (dval >= 0.0) llval = strtoull(s, &p, 0); else llval = strtoll(s, &p, 0); + decimal = TRUE; } if (errno) { @@ -1025,87 +1074,63 @@ value *constVal(const char *s) { werror(W_INVALID_INT_CONST, s, dval); } - /* Setup the flags first */ - /* set the unsigned flag if 'uU' is found */ + // Check suffixes if ((p2 = strchr(p, 'u')) || (p2 = strchr(p, 'U'))) { - SPEC_USIGN(val->type) = 1; + u_suffix = TRUE; p2++; if (strchr(p2, 'u') || strchr(p2, 'U')) werror(E_INTEGERSUFFIX, p); } if ((p2 = strstr(p, "ll")) || (p2 = strstr(p, "LL"))) { - SPEC_NOUN(val->type) = V_INT; - SPEC_LONGLONG(val->type) = 1; - werror(W_LONGLONG_LITERAL, p); + ll_suffix = TRUE; p2 += 2; if (strchr(p2, 'l') || strchr(p2, 'L')) werror(E_INTEGERSUFFIX, p); - } - /* set the b_long flag if 'l' or 'L' is found */ - else if ((p2 = strchr(p, 'l')) || (p2 = strchr(p, 'L'))) { - SPEC_NOUN(val->type) = V_INT; - SPEC_LONG(val->type) = 1; + } else if ((p2 = strchr(p, 'l')) || (p2 = strchr(p, 'L'))) { + l_suffix = TRUE; p2++; if (strchr(p2, 'l') || strchr(p2, 'L')) werror(E_INTEGERSUFFIX, p); - /* c89 allows unsigned long without explicit u suffix */ - if (!options.std_c99 && dval > 0x7fffffff && dval <= 0xffffffff) - SPEC_USIGN(val->type) = 1; + } + + SPEC_NOUN(val->type) = V_INT; + + if (u_suffix) // Choose first of unsigned int, unsigned long int, unsigned + // long long int that fits. + { + SPEC_USIGN(val->type) = 1; + if (ll_suffix || dval > 0xffffffff) + SPEC_LONGLONG(val->type) = 1; + else if (l_suffix || dval > 0xffff) + SPEC_LONG(val->type) = 1; } else { - if (dval < 0) { /* "-28u" will still be signed and negative */ - if (dval < -128) { /* check if we have to promote to int */ - SPEC_NOUN(val->type) = V_INT; - } - if (dval < -32768) { /* check if we have to promote to long int */ + if (decimal) // Choose first of int, long int, long long int that fits. + { + if (ll_suffix || dval > 0x7fffffff || dval < -0x80000000ll) { + if (!options.std_c99) // C90 exception: Use unsigned long + { + SPEC_USIGN(val->type) = 1; + SPEC_LONG(val->type) = 1; + } else + SPEC_LONGLONG(val->type) = 1; + } else if (l_suffix || dval > 0x7fff || dval < -0x8000l) SPEC_LONG(val->type) = 1; - } - if (dval < - -2147483648.0) { /* check if we have to promote to long long int */ + } else // Choose first of int, unsigned int, long int, unsigned long int, + // long long int, unsigned long long int that fits. + { + if (dval > 0x7fffffffffffffff) { + SPEC_USIGN(val->type) = 1; SPEC_LONGLONG(val->type) = 1; - werror(W_LONGLONG_LITERAL, p); - } - } else { /* >=0 */ - if (dval > 0xff || /* check if we have to promote to int */ - SPEC_USIGN(val->type)) { /* If it's unsigned, we can't use unsigned - char. After an integral promotion it will - be a signed int; this certainly isn't what - the programer wants */ - SPEC_NOUN(val->type) = V_INT; - } else { /* store char's always as unsigned; this helps other - optimizations */ + } else if (ll_suffix || dval > 0xffffffff || dval < -0x80000000ll) { + SPEC_LONGLONG(val->type) = 1; + } else if (dval > 0x7fffffff) { SPEC_USIGN(val->type) = 1; - } - if (dval > 0xffff && SPEC_USIGN(val->type) && - !SPEC_LONGLONG(val->type)) { /* check if we have to promote to long */ SPEC_LONG(val->type) = 1; - } else if (dval > 0x7fff && - !SPEC_USIGN( - val->type)) { /* check if we have to promote to long int */ - if (is_integral && - dval <= 0xffff) { /* integral (hex, octal and binary) constants may - be stored in unsigned type */ - SPEC_USIGN(val->type) = 1; - } else { - SPEC_LONG(val->type) = 1; - } - } - if (dval > 0xffffffff && SPEC_USIGN(val->type) && - !SPEC_LONGLONG(val->type)) { - SPEC_LONGLONG(val->type) = 1; - werror(W_LONGLONG_LITERAL, p); - } else if (dval > 0x7fffffff && !SPEC_USIGN(val->type)) { - /* integral constants can be unsigned long. */ - /* c89 also allows unsigned long decimal constants without explicit - * suffix */ - if ((is_integral || !options.std_c99) && dval <= 0xffffffff) { - SPEC_USIGN(val->type) = 1; - } else { - SPEC_LONGLONG(val->type) = 1; - if (is_integral && (llval & 0x8000000000000000ull)) - SPEC_USIGN(val->type) = 1; - werror(W_LONGLONG_LITERAL, p); - } + } else if (l_suffix || dval > 0xffff || dval < -0x8000l) { + SPEC_LONG(val->type) = 1; + } else if (dval > 0x7fff) { + SPEC_USIGN(val->type) = 1; } } } @@ -1150,27 +1175,66 @@ value *constVal(const char *s) { } /*-----------------------------------------------------------------*/ -/* constCharVal - converts a CHAR constant to value */ +/* constCharacterVal - converts a character constant to value */ /*-----------------------------------------------------------------*/ -value *constCharVal(unsigned char v) { +value *constCharacterVal(unsigned long v, char type) { value *val = newValue(); /* alloc space for value */ val->type = val->etype = newLink(SPECIFIER); /* create the specifier */ SPEC_SCLS(val->type) = S_LITERAL; SPEC_CONST(val->type) = 1; - SPEC_NOUN(val->type) = V_CHAR; - - if (options.unsigned_char) { + switch (type) { + case 0: // character constant + SPEC_NOUN(val->type) = V_INT; + SPEC_USIGN(val->type) = 0; + SPEC_CVAL(val->type).v_int = + options.signed_char ? (signed char)v : (unsigned char)v; + break; + case 'L': // wide character constant + if (!options.std_c95) + werror(E_WCHAR_CONST_C95); + SPEC_NOUN(val->type) = V_INT; SPEC_USIGN(val->type) = 1; - SPEC_CVAL(val->type).v_uint = (unsigned char)v; - } else { - SPEC_CVAL(val->type).v_int = (signed char)v; + SPEC_LONG(val->etype) = 1; + SPEC_CVAL(val->type).v_ulong = (TYPE_UDWORD)v; + break; + case 'u': // wide character constant + if (!options.std_c11) + werror(E_WCHAR_CONST_C11); + SPEC_NOUN(val->type) = V_INT; + SPEC_USIGN(val->type) = 1; + SPEC_CVAL(val->type).v_uint = (TYPE_UWORD)v; + break; + case 'U': // wide character constant + if (!options.std_c11) + werror(E_WCHAR_CONST_C11); + SPEC_NOUN(val->type) = V_INT; + SPEC_USIGN(val->type) = 1; + SPEC_LONG(val->etype) = 1; + SPEC_CVAL(val->type).v_ulong = (TYPE_UDWORD)v; + break; + case '8': + if (!options.std_c2x) + werror(E_U8_CHAR_C2X); + if (v >= 128) + werror(E_U8_CHAR_INVALID); + SPEC_NOUN(val->type) = V_CHAR; + SPEC_USIGN(val->type) = 1; + SPEC_CVAL(val->type).v_int = (unsigned char)v; + break; + default: + wassert(0); } return val; } +/*-----------------------------------------------------------------*/ +/* constCharVal - converts a character constant to value */ +/*-----------------------------------------------------------------*/ +value *constCharVal(unsigned char v) { return constCharacterVal(v, 0); } + /*-----------------------------------------------------------------*/ /* constBoolVal - converts a BOOL constant to value */ /*-----------------------------------------------------------------*/ @@ -1188,25 +1252,171 @@ value *constBoolVal(bool v) { return val; } +// TODO: Move this function to SDCCutil? +static const TYPE_UDWORD * +utf_32_from_utf_8(size_t *utf_32_len, const char *utf_8, size_t utf_8_len) { + size_t allocated = 0; + TYPE_UDWORD *utf_32 = 0; + unsigned char first_byte; + TYPE_UDWORD codepoint; + size_t seqlen; + + for (*utf_32_len = 0; utf_8_len; (*utf_32_len)++) { + if (allocated == *utf_32_len) { + utf_32 = realloc(utf_32, sizeof(TYPE_UDWORD) * (*utf_32_len + 16)); + wassert(utf_32); + allocated = *utf_32_len + 16; + } + + first_byte = *utf_8; + seqlen = 1; + if (first_byte & 0x80) { + while (first_byte & (0x80 >> seqlen)) + seqlen++; + first_byte &= (0xff >> (seqlen + 1)); + } + wassert(seqlen <= 6); // seqlen 5 and 6 are deprecated in current unicode + // standard, but for now, allow them. + + codepoint = first_byte; + utf_8++; + utf_8_len--; + seqlen--; + + for (; seqlen; seqlen--) { + codepoint <<= 6; + codepoint |= (*utf_8 & 0x3f); + utf_8++; + utf_8_len--; + } + + utf_32[*utf_32_len] = codepoint; + } + return (utf_32); +} + +// TODO: Move this function to SDCCutil? +static const TYPE_UWORD *utf_16_from_utf_32(size_t *utf_16_len, + const TYPE_UDWORD *utf_32, + size_t utf_32_len) { + size_t allocated = 0; + TYPE_UWORD *utf_16 = 0; + TYPE_UDWORD codepoint; + + for (*utf_16_len = 0; utf_32_len; utf_32_len--, utf_32++) { + if (allocated <= *utf_16_len + 2) { + utf_16 = realloc(utf_16, sizeof(TYPE_UWORD) * (*utf_16_len + 16)); + wassert(utf_16); + allocated = *utf_16_len + 16; + } + + codepoint = *utf_32; + + if (codepoint < 0xd7ff || + codepoint >= 0xe000 && + codepoint <= 0xffff) // Code in basic multilingual plane. + { + utf_16[(*utf_16_len)++] = codepoint; + continue; + } + + // Code point in supplementary plane. + wassert(codepoint >= 0x100000 && codepoint <= 0x10ffff); + codepoint -= 0x100000; + + utf_16[(*utf_16_len)++] = ((codepoint >> 10) & 0x3ff) + 0xd800; + utf_16[(*utf_16_len)++] = (codepoint & 0x3ff) + 0xdc00; + } + + return (utf_16); +} + /*------------------------------------------------------------------*/ /* strVal - converts a string constant to a value */ /*------------------------------------------------------------------*/ value *strVal(const char *s) { value *val; + const char *utf_8; + size_t utf_8_size; - val = newValue(); /* get a new one */ + val = newValue(); /* get a declarator */ val->type = newLink(DECLARATOR); DCL_TYPE(val->type) = ARRAY; val->type->next = val->etype = newLink(SPECIFIER); - SPEC_NOUN(val->etype) = V_CHAR; SPEC_SCLS(val->etype) = S_LITERAL; SPEC_CONST(val->etype) = 1; - SPEC_CVAL(val->etype).v_char = copyStr(s, &DCL_ELEM(val->type)); + // Convert input string (mixed UTF-8 and UTF-32) to UTF-8 first (handling all + // escape sequences, etc). + utf_8 = copyStr(s[0] == '"' ? s : s + 1, &utf_8_size); - return val; + if (s[0] == '"') // UTF-8 string literal (any prefix u8 or L in the source + // would already have been stripped by earlier stages) + { + SPEC_NOUN(val->etype) = V_CHAR; + SPEC_USIGN(val->etype) = !options.signed_char; + val->etype->select.s.b_implicit_sign = true; + SPEC_CVAL(val->etype).v_char = utf_8; + DCL_ELEM(val->type) = utf_8_size; + } else { + size_t utf_32_size; + // Convert to UTF-32 next, since converting UTF-32 to UTF-16 is easier than + // UTF-8 to UTF-16. + const TYPE_UDWORD *utf_32 = + utf_32_from_utf_8(&utf_32_size, utf_8, utf_8_size); + + dbuf_free(utf_8); + + if (s[0] == 'U' || s[0] == 'L') // UTF-32 string literal + { + SPEC_NOUN(val->etype) = V_INT; + SPEC_USIGN(val->etype) = 1; + SPEC_LONG(val->etype) = 1; + SPEC_CVAL(val->etype).v_char32 = utf_32; + DCL_ELEM(val->type) = utf_32_size; + } else if (s[0] == 'u') // UTF-16 string literal + { + size_t utf_16_size; + const TYPE_UWORD *utf_16 = + utf_16_from_utf_32(&utf_16_size, utf_32, utf_32_size); + + SPEC_NOUN(val->etype) = V_INT; + SPEC_USIGN(val->etype) = 1; + SPEC_CVAL(val->etype).v_char16 = utf_16; + DCL_ELEM(val->type) = utf_16_size; + } else + wassert(0); + } + + return (val); +} + +/*------------------------------------------------------------------*/ +/* rawStrVal - converts a string to a value */ +/*------------------------------------------------------------------*/ +value *rawStrVal(const char *s, size_t size) { + struct dbuf_s dbuf; + value *val = newValue(); + + dbuf_init(&dbuf, size); + wassert(dbuf_append(&dbuf, s, size)); + + /* get a declarator */ + val->type = newLink(DECLARATOR); + DCL_TYPE(val->type) = ARRAY; + val->type->next = val->etype = newLink(SPECIFIER); + SPEC_SCLS(val->etype) = S_LITERAL; + SPEC_CONST(val->etype) = 1; + + SPEC_NOUN(val->etype) = V_CHAR; + SPEC_USIGN(val->etype) = !options.signed_char; + val->etype->select.s.b_implicit_sign = true; + SPEC_CVAL(val->etype).v_char = dbuf_detach(&dbuf); + DCL_ELEM(val->type) = size; + + return (val); } /*------------------------------------------------------------------*/ @@ -1290,33 +1500,46 @@ value *copyValue(value *src) { /* charVal - converts a character constant to a value */ /*------------------------------------------------------------------*/ value *charVal(const char *s) { - /* get rid of quotation */ + char type; + + if ((s[0] == 'L' || s[0] == 'u' || s[0] == 'U') && s[1] == '\'') + type = *s++; + else if (s[0] == 'u' && s[1] == '8' && s[2] == '\'') { + if (s[4] != '\'') + werror(E_U8_CHAR_INVALID); + type = '8'; + s += 2; + } else + type = 0; + + s++; // Get rid of quotation. + /* if \ then special processing */ - if (*++s == '\\') { + if (*s == '\\') { switch (*++s) /* go beyond the backslash */ { case 'n': - return constCharVal('\n'); + return constCharacterVal('\n', type); case 't': - return constCharVal('\t'); + return constCharacterVal('\t', type); case 'v': - return constCharVal('\v'); + return constCharacterVal('\v', type); case 'b': - return constCharVal('\b'); + return constCharacterVal('\b', type); case 'r': - return constCharVal('\r'); + return constCharacterVal('\r', type); case 'f': - return constCharVal('\f'); + return constCharacterVal('\f', type); case 'a': - return constCharVal('\a'); + return constCharacterVal('\a', type); case '\\': - return constCharVal('\\'); + return constCharacterVal('\\', type); case '\?': - return constCharVal('\?'); + return constCharacterVal('\?', type); case '\'': - return constCharVal('\''); + return constCharacterVal('\'', type); case '\"': - return constCharVal('\"'); + return constCharacterVal('\"', type); case '0': case '1': @@ -1326,22 +1549,30 @@ value *charVal(const char *s) { case '5': case '6': case '7': - return constCharVal(octalEscape(&s)); + return constCharacterVal(octalEscape(&s), type); case 'x': - return constCharVal(hexEscape(&s)); + return constCharacterVal(hexEscape(&s), type); case 'u': - return constCharVal(universalEscape(&s, 4)); + return constCharacterVal(universalEscape(&s, 4), type); case 'U': - return constCharVal(universalEscape(&s, 8)); + return constCharacterVal(universalEscape(&s, 8), type); default: - return constCharVal(*s); + return constCharacterVal(*s, type); } - } else /* not a backslash */ - return constCharVal(*s); + } else if (type) // Wide character constant + { + size_t ulen; + const TYPE_UDWORD *ustr = utf_32_from_utf_8(&ulen, s, strlen(s) - 1); + value *val = constCharacterVal(*ustr, type); + free((void *)ustr); + return (val); + } else // Character constant that is not wide - compability with legacy + // encodings. + return constCharacterVal(*s, 0); } /*------------------------------------------------------------------*/ @@ -1422,7 +1653,7 @@ double floatFromVal(value *val) { /*------------------------------------------------------------------*/ /* ulFromVal - value to unsigned long conversion */ /*------------------------------------------------------------------*/ -unsigned long ulFromVal(value *val) { +unsigned long ulFromVal(const value *val) { if (!val) return 0; @@ -1494,6 +1725,8 @@ unsigned char byteOfVal(value *val, int offset) { unsigned char *p; int shift = 8 * offset; + wassert(offset >= 0); + if (!val) return 0; @@ -1568,7 +1801,7 @@ unsigned char byteOfVal(value *val, int offset) { } /*------------------------------------------------------------------*/ -/* ullFromLit - literal to unsigned long conversion */ +/* ullFromLit - literal to unsigned long long conversion */ /*------------------------------------------------------------------*/ TYPE_TARGET_ULONGLONG ullFromLit(sym_link *lit) { @@ -1637,10 +1870,9 @@ ullFromLit(sym_link *lit) { } /*------------------------------------------------------------------*/ -/* ullFromVal - value to unsigned long conversion */ +/* ullFromVal - value to unsigned long long conversion */ /*------------------------------------------------------------------*/ -TYPE_TARGET_ULONGLONG -ullFromVal(value *val) { +unsigned long long ullFromVal(value *val) { if (!val) return 0; @@ -1648,7 +1880,43 @@ ullFromVal(value *val) { werror(E_CONST_EXPECTED, val->name); return 0; } - return ullFromLit(val->type); + return (unsigned long long)ullFromLit(val->type); +} + +/*------------------------------------------------------------------*/ +/* csdOfVal - return 0 if the value can be represented as csd */ +/* topbit - highest nonzero bit in csd */ +/* nonzero - number of nonzero bits in csd */ +/* csd_add - positive bits in csd */ +/* csd_sub - negative bits in csd */ +/*------------------------------------------------------------------*/ +int csdOfVal(int *topbit, int *nonzero, unsigned long long *csd_add, + unsigned long long *csd_sub, value *val) { + unsigned long long binary = ullFromVal(val); + bool gamma, theta, a; + int bit, next; + + *topbit = 0; + *nonzero = 0; + *csd_add = 0; + *csd_sub = 0; + + for (a = 0, gamma = 0, bit = 0; bit < 61; bit++) { + theta = a ^ (binary & 1); + gamma = !gamma && theta; + next = (1 - 2 * (bool)(binary & 2)) * gamma; + if (next > 0) + *csd_add |= (1ull << bit); + else if (next < 0) + *csd_sub |= (1ull << bit); + if (next) { + (*nonzero)++; + *topbit = bit; + } + a = (binary & 1); + binary >>= 1; + } + return ((bool)binary); } /*------------------------------------------------------------------*/ @@ -1863,7 +2131,8 @@ value *valMult(value *lval, value *rval) { value *valDiv(value *lval, value *rval) { value *val; - if (isEqualVal(rval, 0)) { + if (isEqualVal(rval, 0) && + !IS_FLOAT(computeType(lval->etype, rval->etype, RESULT_TYPE_INT, '/'))) { werror(E_DIVIDE_BY_ZERO); return rval; } @@ -2055,7 +2324,8 @@ value *valShift(value *lval, value *rval, int lr) { /* left shift */ (lr || /* right shift and unsigned */ - (!lr && SPEC_USIGN(rval->type)))) { + (!lr && SPEC_USIGN(rval->type))) && + ((TYPE_TARGET_ULONG)ulFromVal(lval) != (TYPE_TARGET_ULONG)0)) { werror(W_SHIFT_CHANGED, (lr ? "left" : "right")); } @@ -2326,7 +2596,8 @@ value *valLogicAndOr(value *lval, value *rval, int op) { /*------------------------------------------------------------------*/ /* valCastLiteral - casts a literal value to another type */ /*------------------------------------------------------------------*/ -value *valCastLiteral(sym_link *dtype, double fval) { +value *valCastLiteral(sym_link *dtype, double fval, + TYPE_TARGET_ULONGLONG llval) { value *val; unsigned long l = double2ul(fval); @@ -2382,9 +2653,9 @@ value *valCastLiteral(sym_link *dtype, double fval) { default: if (SPEC_LONGLONG(val->etype)) { if (SPEC_USIGN(val->etype)) - SPEC_CVAL(val->etype).v_ulonglong = (TYPE_TARGET_ULONGLONG)l; + SPEC_CVAL(val->etype).v_ulonglong = (TYPE_TARGET_ULONGLONG)llval; else - SPEC_CVAL(val->etype).v_longlong = (TYPE_TARGET_LONGLONG)l; + SPEC_CVAL(val->etype).v_longlong = (TYPE_TARGET_LONGLONG)llval; } else if (SPEC_LONG(val->etype)) { if (SPEC_USIGN(val->etype)) SPEC_CVAL(val->etype).v_ulong = (TYPE_TARGET_ULONG)l; @@ -2414,7 +2685,7 @@ value *valRecastLitVal(sym_link *dtype, value *val) { fval = floatFromVal(val); ull = (TYPE_TARGET_ULONGLONG)fval; } else { - ull = ullFromVal(val); + ull = (TYPE_TARGET_ULONGLONG)ullFromVal(val); fval = (double)ull; } @@ -2499,11 +2770,15 @@ int getNelements(sym_link *type, initList *ilist) { /* if type is a character array and there is only one (string) initialiser then get the length of the string */ - if (IS_ARRAY(type) && IS_CHAR(type->next) && !ilist->next) { + if (IS_ARRAY(type) && + (IS_CHAR(type->next) || IS_INT(type->next) && IS_UNSIGNED(type->next)) && + !ilist->next) { ast *iast = ilist->init.node; value *v = (iast->type == EX_VALUE ? iast->opval.val : NULL); - if (v && IS_ARRAY(v->type) && IS_CHAR(v->etype)) + if (v && IS_ARRAY(v->type) && + (IS_CHAR(v->etype) || IS_INT(v->etype) && IS_UNSIGNED(v->etype) && + IS_LONG(type->next) == IS_LONG(v->etype))) /* yep, it's a string */ { return DCL_ELEM(v->type); @@ -2599,12 +2874,33 @@ value *valForArray(ast *arrExpr) { value *valForStructElem(ast *structT, ast *elemT) { value *val, *lval = NULL; symbol *sym; + int idxoff = 0; + ast *sast = NULL; /* left could be further derefed */ if (IS_AST_OP(structT)) { if (structT->opval.op == '[') lval = valForArray(structT); - else if (structT->opval.op == '.') + else if (structT->opval.op == '+') { + if (IS_AST_LIT_VALUE(structT->right) && !IS_AST_OP(structT->left)) { + idxoff = (int)(AST_ULONG_VALUE(structT->right) * + getSize(structT->left->ftype->next)); + sast = structT->left; + } else if (IS_AST_LIT_VALUE(structT->left) && + !IS_AST_OP(structT->right)) { + idxoff = (int)(AST_ULONG_VALUE(structT->left) * + getSize(structT->right->ftype->next)); + sast = structT->right; + } else + return NULL; + } else if (structT->opval.op == '-') { + if (IS_AST_LIT_VALUE(structT->right) && !IS_AST_OP(structT->left)) { + idxoff = 0 - (int)(AST_ULONG_VALUE(structT->right) * + getSize(structT->left->ftype->next)); + sast = structT->left; + } else + return NULL; + } else if (structT->opval.op == '.') lval = valForStructElem(structT->left, structT->right); else if (structT->opval.op == PTR_OP) { if (IS_ADDRESS_OF_OP(structT->left)) @@ -2637,8 +2933,13 @@ value *valForStructElem(ast *structT, ast *elemT) { (int)sym->offset); memcpy(val->type, lval->type, sizeof(sym_link)); } else { - SNPRINTF(val->name, sizeof(val->name), "(%s + %d)", - AST_SYMBOL(structT)->rname, (int)sym->offset); + if (sast) + SNPRINTF(val->name, sizeof(val->name), "(%s + (%d))", + AST_SYMBOL(sast)->rname, ((int)sym->offset) + idxoff); + else + SNPRINTF(val->name, sizeof(val->name), "(%s + %d)", + AST_SYMBOL(structT)->rname, (int)sym->offset); + if (SPEC_SCLS(structT->etype) == S_CODE) DCL_TYPE(val->type) = CPOINTER; else if (SPEC_SCLS(structT->etype) == S_XDATA) @@ -2681,8 +2982,8 @@ value *valForCastAggr(ast *aexpr, sym_link *type, ast *cnst, int op) { } /*-----------------------------------------------------------------*/ -/* valForCastAggr - will return value for a cast of an aggregate */ -/* with no constant */ +/* valForCastArr - will return value for a cast of an aggregate */ +/* with no constant */ /*-----------------------------------------------------------------*/ value *valForCastArr(ast *aexpr, sym_link *type) { value *val; diff --git a/src/SDCCval.h b/src/SDCCval.h index 77f4541d5..3aec8ea1c 100644 --- a/src/SDCCval.h +++ b/src/SDCCval.h @@ -36,7 +36,11 @@ typedef struct value { } value; typedef struct literalList { - double literalValue; + bool isFloat; + union { + unsigned long long ull; + double f64; + } value; unsigned count; struct literalList *next; } literalList; @@ -85,18 +89,22 @@ typedef enum { /* forward definitions for the symbol table related functions */ value *newValue(void); value *constVal(const char *); -value *constCharVal(unsigned char); +value *constIntVal(const char *); +value *constCharacterVal(unsigned long v, char type); +value *constCharVal(unsigned char v); value *constBoolVal(bool v); value *reverseVal(value *); value *reverseValWithType(value *); value *copyValue(value *); value *copyValueChain(value *); value *strVal(const char *); +value *rawStrVal(const char *, size_t size); value *charVal(const char *); value *symbolVal(symbol *); void printVal(value *); double floatFromVal(value *); -unsigned long ulFromVal(value *); +unsigned long ulFromVal(const value *); +unsigned long long ullFromVal(value *); /* convert a fixed16x16 type to double */ double doubleFromFixed16x16(TYPE_TARGET_ULONG value); @@ -119,13 +127,13 @@ value *valShift(value *, value *, int); value *valCompare(value *, value *, int); value *valBitwise(value *, value *, int); value *valLogicAndOr(value *, value *, int); -value *valCastLiteral(sym_link *, double); +value *valCastLiteral(sym_link *, double, TYPE_TARGET_ULONGLONG); value *valueFromLit(double); initList *newiList(int, void *); initList *revinit(initList *); initList *copyIlist(initList *); double list2int(initList *); -value *list2val(initList *); +value *list2val(initList *, int); struct ast *list2expr(initList *); void resolveIvalSym(initList *, sym_link *); designation *newDesignation(int, void *); @@ -144,6 +152,8 @@ bool convertIListToConstList(initList *src, literalList **lList, int size); literalList *copyLiteralList(literalList *src); unsigned long double2ul(double val); unsigned char byteOfVal(value *, int); +int csdOfVal(int *topbit, int *nonzero, unsigned long long *csd_add, + unsigned long long *csd_sub, value *val); int isEqualVal(value *, int); TYPE_TARGET_ULONGLONG ullFromLit(sym_link *lit); value *valRecastLitVal(sym_link *dtype, value *val); diff --git a/src/backend/gen.c b/src/backend/gen.c index 26f60fa23..ef646e801 100644 --- a/src/backend/gen.c +++ b/src/backend/gen.c @@ -4,7 +4,7 @@ Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com Copyright (C) 2000, Michael Hope - Copyright (C) 2011-2012, Philipp Klaus Krause pkk@spth.de, + Copyright (C) 2011-2018, Philipp Klaus Krause pkk@spth.de, philipp@informatik.uni-frankfurt.de) This program is free software; you can redistribute it and/or modify it @@ -28,8 +28,8 @@ philipp@informatik.uni-frankfurt.de) #include #include "../util/dbuf_string.h" -#include "gen.h" #include "z80.h" +#include "gen.h" /* This is the down and dirty file with all kinds of kludgy & hacky stuff. This is what it is all about CODE GENERATION for a specific MCU. @@ -55,14 +55,7 @@ enum { DISABLE_DEBUG = 0 }; -//#define DEBUG_DRY_COST - -static char *_z80_return[] = {"l", "h", "e", "d"}; -static char *_gbz80_return[] = {"e", "d", "l", "h"}; -static char *_fReceive[] = {"c", "b", "e", "d"}; - -static char **_fReturn; -static char **_fTmp; +// #define DEBUG_DRY_COST extern struct dbuf_s *codeOutBuf; @@ -85,13 +78,13 @@ static struct { const char *name; const char *l; const char *h; -} _pairs[NUM_PAIRS] = {{"??1", "?2", "?3"}, {"af", "f", "a"}, - {"bc", "c", "b"}, {"de", "e", "d"}, - {"hl", "l", "h"}, {"iy", "iyl", "iyh"}, - {"ix", "ixl", "ixh"}}; - -// PENDING -#define ACC_NAME _pairs[PAIR_AF].h + int l_idx; + int h_idx; +} _pairs[NUM_PAIRS] = { + {"??1", "?2", "?3", -1, -1}, {"af", "f", "a", -1, A_IDX}, + {"bc", "c", "b", C_IDX, B_IDX}, {"de", "e", "d", E_IDX, D_IDX}, + {"hl", "l", "h", L_IDX, H_IDX}, {"iy", "iyl", "iyh", IYL_IDX, IYH_IDX}, + {"ix", "ixl", "ixh", -1, -1}}; enum { LSB, MSB16, MSB24, MSB32 }; @@ -104,12 +97,14 @@ enum asminst { A_DEC, A_INC, A_LD, + A_NEG, A_OR, A_RL, A_RLA, A_RLC, A_RLCA, A_RR, + A_RRA, A_RRC, A_RRCA, A_SBC, @@ -121,10 +116,10 @@ enum asminst { A_SWAP }; -static const char *asminstnames[] = {"add", "adc", "and", "cp", "cpl", "dec", - "inc", "ld", "or", "rl", "rla", "rlc", - "rlca", "rr", "rrc", "rrca", "sbc", "sla", - "sra", "srl", "sub", "xor", "swap"}; +static const char *asminstnames[] = { + "add", "adc", "and", "cp", "cpl", "dec", "inc", "ld", "neg", + "or", "rl", "rla", "rlc", "rlca", "rr", "rra", "rrc", "rrca", + "sbc", "sla", "sra", "srl", "sub", "xor", "swap"}; /** Code generator persistent data. */ @@ -178,10 +173,15 @@ static struct { } trace; } _G; +bool z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX + 1]; +bool z80_symmParm_in_calls_from_current_function; +bool z80_regs_preserved_in_calls_from_current_function[IYH_IDX + 1]; + static const char *aopGet(asmop *aop, int offset, bool bit16); static struct asmop asmop_a, asmop_b, asmop_c, asmop_d, asmop_e, asmop_h, - asmop_l, asmop_iyh, asmop_iyl, asmop_zero, asmop_one; + asmop_l, asmop_iyh, asmop_iyl, asmop_hl, asmop_de, asmop_bc, asmop_zero, + asmop_one, asmop_return; static struct asmop *const ASMOP_A = &asmop_a; static struct asmop *const ASMOP_B = &asmop_b; static struct asmop *const ASMOP_C = &asmop_c; @@ -191,71 +191,196 @@ static struct asmop *const ASMOP_H = &asmop_h; static struct asmop *const ASMOP_L = &asmop_l; static struct asmop *const ASMOP_IYH = &asmop_iyh; static struct asmop *const ASMOP_IYL = &asmop_iyl; +static struct asmop *const ASMOP_HL = &asmop_hl; +static struct asmop *const ASMOP_DE = &asmop_de; +static struct asmop *const ASMOP_BC = &asmop_bc; static struct asmop *const ASMOP_ZERO = &asmop_zero; static struct asmop *const ASMOP_ONE = &asmop_one; +static struct asmop *const ASMOP_RETURN = &asmop_return; -static asmop *_z80_return3[] = {&asmop_l, &asmop_h, &asmop_e, &asmop_d}; -static asmop *_gbz80_return3[] = {&asmop_e, &asmop_d, &asmop_l, &asmop_h}; - -static asmop *asmopregs[] = {&asmop_c, &asmop_b, &asmop_e, &asmop_d, - &asmop_l, &asmop_h, &asmop_iyl, &asmop_iyh}; - -static asmop **_fReturn3; +static asmop *asmopregs[] = {&asmop_a, &asmop_c, &asmop_b, + &asmop_e, &asmop_d, &asmop_l, + &asmop_h, &asmop_iyl, &asmop_iyh}; void z80_init_asmops(void) { - asmop_a.type = AOP_ACC; + asmop_a.type = AOP_REG; asmop_a.size = 1; - asmop_a.aopu.aop_str[0] = "a"; - + asmop_a.aopu.aop_reg[0] = regsZ80 + A_IDX; + memset(asmop_a.regs, -1, 9); + asmop_a.regs[A_IDX] = 0; asmop_b.type = AOP_REG; asmop_b.size = 1; asmop_b.aopu.aop_reg[0] = regsZ80 + B_IDX; + memset(asmop_b.regs, -1, 9); + asmop_b.regs[B_IDX] = 0; asmop_c.type = AOP_REG; asmop_c.size = 1; asmop_c.aopu.aop_reg[0] = regsZ80 + C_IDX; + memset(asmop_c.regs, -1, 9); + asmop_c.regs[C_IDX] = 0; asmop_d.type = AOP_REG; asmop_d.size = 1; asmop_d.aopu.aop_reg[0] = regsZ80 + D_IDX; + memset(asmop_d.regs, -1, 9); + asmop_d.regs[D_IDX] = 0; asmop_e.type = AOP_REG; asmop_e.size = 1; asmop_e.aopu.aop_reg[0] = regsZ80 + E_IDX; + memset(asmop_e.regs, -1, 9); + asmop_e.regs[E_IDX] = 0; asmop_h.type = AOP_REG; asmop_h.size = 1; asmop_h.aopu.aop_reg[0] = regsZ80 + H_IDX; + memset(asmop_h.regs, -1, 9); + asmop_h.regs[H_IDX] = 0; asmop_l.type = AOP_REG; asmop_l.size = 1; asmop_l.aopu.aop_reg[0] = regsZ80 + L_IDX; + memset(asmop_l.regs, -1, 9); + asmop_l.regs[L_IDX] = 0; asmop_iyh.type = AOP_REG; asmop_iyh.size = 1; asmop_iyh.aopu.aop_reg[0] = regsZ80 + IYH_IDX; + memset(asmop_iyh.regs, -1, 9); + asmop_iyh.regs[IYH_IDX] = 0; asmop_iyl.type = AOP_REG; asmop_iyl.size = 1; asmop_iyl.aopu.aop_reg[0] = regsZ80 + IYL_IDX; - - asmop_zero.type = AOP_SIMPLELIT; - asmop_zero.aopu.aop_simplelit = 0; + memset(asmop_iyl.regs, -1, 9); + asmop_iyl.regs[IYL_IDX] = 0; + + asmop_hl.type = AOP_REG; + asmop_hl.size = 2; + asmop_hl.aopu.aop_reg[0] = regsZ80 + L_IDX; + asmop_hl.aopu.aop_reg[1] = regsZ80 + H_IDX; + memset(asmop_hl.regs, -1, 9); + asmop_hl.regs[L_IDX] = 0; + asmop_hl.regs[H_IDX] = 1; + + asmop_de.type = AOP_REG; + asmop_de.size = 2; + asmop_de.aopu.aop_reg[0] = regsZ80 + E_IDX; + asmop_de.aopu.aop_reg[1] = regsZ80 + D_IDX; + memset(asmop_de.regs, -1, 9); + asmop_de.regs[E_IDX] = 0; + asmop_de.regs[D_IDX] = 1; + + asmop_bc.type = AOP_REG; + asmop_bc.size = 2; + asmop_bc.aopu.aop_reg[0] = regsZ80 + C_IDX; + asmop_bc.aopu.aop_reg[1] = regsZ80 + B_IDX; + memset(asmop_bc.regs, -1, 9); + asmop_bc.regs[C_IDX] = 0; + asmop_bc.regs[B_IDX] = 1; + + asmop_zero.type = AOP_LIT; + asmop_zero.aopu.aop_lit = constVal("0"); asmop_zero.size = 1; + memset(asmop_zero.regs, -1, 9); - asmop_one.type = AOP_SIMPLELIT; - asmop_one.aopu.aop_simplelit = 1; + asmop_one.type = AOP_LIT; + asmop_one.aopu.aop_lit = constVal("1"); asmop_one.size = 1; + memset(asmop_one.regs, -1, 9); - _fReturn3 = IS_GB ? _gbz80_return3 : _z80_return3; + asmop_return.type = AOP_REG; + asmop_return.size = 4; + memset(asmop_return.regs, -1, 9); + asmop_return.aopu.aop_reg[0] = regsZ80 + L_IDX; + asmop_return.regs[L_IDX] = 0; + asmop_return.aopu.aop_reg[1] = regsZ80 + H_IDX; + asmop_return.regs[H_IDX] = 1; + asmop_return.aopu.aop_reg[2] = regsZ80 + E_IDX; + asmop_return.regs[E_IDX] = 2; + asmop_return.aopu.aop_reg[3] = regsZ80 + D_IDX; + asmop_return.regs[D_IDX] = 3; } static bool regalloc_dry_run; -static unsigned char regalloc_dry_run_cost; +static unsigned int regalloc_dry_run_cost; static void cost(unsigned int bytes, unsigned int cycles) { regalloc_dry_run_cost += bytes; } -static void cost2(unsigned int bytes, unsigned int cycles_z80, - unsigned int cycles_z180, unsigned int cycles_rab, - unsigned int cycles_gbz80, unsigned int cycles_tlcs90) { +static void cost2(unsigned int bytes, unsigned int z80_states, + unsigned int z180_states, unsigned int r2k_clocks, + unsigned int cycles_gbz80, unsigned int tlcs90_states, + unsigned int ez80_z80_cycles) { regalloc_dry_run_cost += bytes; } +/*-----------------------------------------------------------------*/ +/* aopRS - asmop in register or on stack */ +/*-----------------------------------------------------------------*/ +static bool aopRS(const asmop *aop) { + return (aop->type == AOP_REG || aop->type == AOP_STK || + aop->type == AOP_EXSTK); +} + +/*-----------------------------------------------------------------*/ +/* aopIsLitVal - asmop from offset is val */ +/*-----------------------------------------------------------------*/ +static bool aopIsLitVal(const asmop *aop, int offset, int size, + unsigned long long int val) { + wassert(size <= + sizeof(unsigned long long int)); // Make sure we are not testing + // outside of argument val. + + for (; size; size--, offset++) { + unsigned char b = val & 0xff; + val >>= 8; + + // Leading zeroes + if (aop->size <= offset && !b && aop->type != AOP_LIT) + continue; + + if (aop->type != AOP_LIT) + return (false); + + if (byteOfVal(aop->aopu.aop_lit, offset) != b) + return (false); + } + + return (true); +} + +/*-----------------------------------------------------------------*/ +/* aopInReg - asmop from offset in the register */ +/*-----------------------------------------------------------------*/ +static inline bool aopInReg(const asmop *aop, int offset, short rIdx) { + if (!(aop->type == AOP_REG)) + return (false); + + if (offset >= aop->size || offset < 0) + return (false); + + if (rIdx == IY_IDX) + return (aopInReg(aop, offset, IYL_IDX) && + aopInReg(aop, offset + 1, IYH_IDX)); + if (rIdx == BC_IDX) + return (aopInReg(aop, offset, C_IDX) && aopInReg(aop, offset + 1, B_IDX)); + if (rIdx == DE_IDX) + return (aopInReg(aop, offset, E_IDX) && aopInReg(aop, offset + 1, D_IDX)); + if (rIdx == HL_IDX) + return (aopInReg(aop, offset, L_IDX) && aopInReg(aop, offset + 1, H_IDX)); + + return (aop->aopu.aop_reg[offset]->rIdx == rIdx); +} + +/*-----------------------------------------------------------------*/ +/* aopOnStack - asmop from offset on stack in consecutive memory */ +/*-----------------------------------------------------------------*/ +static bool aopOnStack(const asmop *aop, int offset, int size) { + if (!(aop->type == AOP_STK || aop->type == AOP_EXSTK)) + return (false); + + if (offset + size > aop->size) + return (false); + + return (true); +} + /* WARNING: This function is dangerous to use. It works literally: It will return true if ic the the last use of op, even if ic might be executed again, e.g. due to a loop. Most of the time you will want @@ -278,11 +403,7 @@ static bool isLastUse(const iCode *ic, operand *op) { } static PAIR_ID _getTempPairId(void) { - if (IS_GB) { - return PAIR_DE; - } else { return PAIR_HL; - } } static const char *_getTempPairName(void) { @@ -328,18 +449,17 @@ static bool isPairDead(PAIR_ID id, const iCode *ic) { static PAIR_ID getDeadPairId(const iCode *ic) { if (isPairDead(PAIR_BC, ic)) { return PAIR_BC; - } else if (!IS_GB && isPairDead(PAIR_DE, ic)) { + } else if (isPairDead(PAIR_DE, ic)) { return PAIR_DE; } else { return PAIR_INVALID; } } -static PAIR_ID getFreePairId(const iCode *ic) // Todo: Cost -{ +static PAIR_ID getFreePairId(const iCode *ic) { if (!isPairInUse(PAIR_BC, ic)) { return PAIR_BC; - } else if (!IS_GB && !isPairInUse(PAIR_DE, ic)) { + } else if (!isPairInUse(PAIR_DE, ic)) { return PAIR_DE; } else { return PAIR_INVALID; @@ -432,28 +552,32 @@ static PAIR_ID getPartPairId(const asmop *aop, int offset) { return PAIR_INVALID; } -static PAIR_ID getPairId(const asmop *aop) { - if (aop->size == 2) { +static PAIR_ID getPairId_o(const asmop *aop, int offset) { + if (offset >= 0 && offset + 2 <= aop->size) { if (aop->type == AOP_REG) { - wassert(aop->aopu.aop_reg[0] && aop->aopu.aop_reg[1]); + wassert(aop->aopu.aop_reg[offset] && aop->aopu.aop_reg[offset + 1]); - if ((aop->aopu.aop_reg[0]->rIdx == C_IDX) && - (aop->aopu.aop_reg[1]->rIdx == B_IDX)) { + if ((aop->aopu.aop_reg[offset]->rIdx == C_IDX) && + (aop->aopu.aop_reg[offset + 1]->rIdx == B_IDX)) { return PAIR_BC; } - if ((aop->aopu.aop_reg[0]->rIdx == E_IDX) && - (aop->aopu.aop_reg[1]->rIdx == D_IDX)) { + if ((aop->aopu.aop_reg[offset]->rIdx == E_IDX) && + (aop->aopu.aop_reg[offset + 1]->rIdx == D_IDX)) { return PAIR_DE; } - if ((aop->aopu.aop_reg[0]->rIdx == L_IDX) && - (aop->aopu.aop_reg[1]->rIdx == H_IDX)) { + if ((aop->aopu.aop_reg[offset]->rIdx == L_IDX) && + (aop->aopu.aop_reg[offset + 1]->rIdx == H_IDX)) { return PAIR_HL; } - } else if (aop->type == AOP_STR || aop->type == AOP_HLREG) { + if ((aop->aopu.aop_reg[offset]->rIdx == IYL_IDX) && + (aop->aopu.aop_reg[offset + 1]->rIdx == IYH_IDX)) { + return PAIR_IY; + } + } else if (aop->type == AOP_STR) { int i; for (i = 0; i < NUM_PAIRS; i++) { - if (!strcmp(aop->aopu.aop_str[0], _pairs[i].l) && - !strcmp(aop->aopu.aop_str[1], _pairs[i].h)) + if (!strcmp(aop->aopu.aop_str[offset], _pairs[i].l) && + !strcmp(aop->aopu.aop_str[offset + 1], _pairs[i].h)) return i; } } @@ -461,6 +585,12 @@ static PAIR_ID getPairId(const asmop *aop) { return PAIR_INVALID; } +static PAIR_ID getPairId(const asmop *aop) { + if (aop->size != 2) + return PAIR_INVALID; + return (getPairId_o(aop, 0)); +} + /*-----------------------------------------------------------------*/ /* z80_emitDebuggerSymbol - associate the current code location */ /* with a debugger symbol */ @@ -472,15 +602,14 @@ void z80_emitDebuggerSymbol(const char *debugSym) { genLine.lineElement.isDebug = 0; } -// Todo: Handle IY (when used as AOP_HLREG or AOP_REG) correctly. -static unsigned char ld_cost(asmop *op1, asmop *op2) { +// Todo: Handle IY correctly. +static unsigned char ld_cost(const asmop *op1, const asmop *op2) { AOP_TYPE op1type = op1->type; AOP_TYPE op2type = op2->type; /* Costs are symmetric */ - if (op2type == AOP_ACC || op2type == AOP_REG || op2type == AOP_HLREG || - op2type == AOP_DUMMY) { - asmop *tmp = op1; + if (op2type == AOP_REG || op2type == AOP_DUMMY) { + const asmop *tmp = op1; op1 = op2; op2 = tmp; op1type = op1->type; @@ -488,22 +617,17 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { } switch (op1type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: switch (op2type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: return (1); case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (2); case AOP_SFR: /* 2 from in a, (...) */ - return ((op1type == AOP_ACC || op1type == AOP_DUMMY) ? 2 : 3); + return ((aopInReg(op1, 0, A_IDX) || op1type == AOP_DUMMY) ? 2 : 3); case AOP_STK: return (3); case AOP_HL: /* 3 from ld hl, #... */ @@ -517,21 +641,18 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) return (3); if (op2->aopu.aop_pairId == PAIR_BC || op2->aopu.aop_pairId == PAIR_DE) - return ((op1type == AOP_ACC || op1type == AOP_DUMMY) ? 1 : 2); + return ((aopInReg(op1, 0, A_IDX) || op1type == AOP_DUMMY) ? 1 : 2); default: - printf("ld_cost op1: AOP_REG, op2: %d", (int)(op2type)); + printf("ld_cost op1: AOP_REG, op2: %d\n", (int)(op2type)); wassert(0); } case AOP_SFR: /* 2 from out (...), a */ switch (op2type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: return (2); case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (4); case AOP_STK: return (5); @@ -543,7 +664,7 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { case AOP_EXSTK: /* 4 from ld iy, #... */ return (9); default: - printf("ld_cost op1: AOP_SFR, op2: %d", (int)(op2type)); + printf("ld_cost op1: AOP_SFR, op2: %d\n", (int)(op2type)); wassert(0); } case AOP_IY: /* 4 from ld iy, #... */ @@ -551,7 +672,6 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { switch (op2type) { case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (8); case AOP_SFR: /* 2 from in a, (...) */ return (9); @@ -562,14 +682,13 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { case AOP_EXSTK: return (16); default: - printf("ld_cost op1: AOP_IY, op2: %d", (int)(op2type)); + printf("ld_cost op1: AOP_IY, op2: %d\n", (int)(op2type)); wassert(0); } case AOP_STK: switch (op2type) { case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (4); case AOP_SFR: /* 2 from in a, (...) */ return (5); @@ -587,22 +706,20 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) return (6); default: - printf("ld_cost op1: AOP_STK, op2: %d", (int)(op2type)); + printf("ld_cost op1: AOP_STK, op2: %d\n", (int)(op2type)); wassert(0); } case AOP_HL: /* 3 from ld hl, #... */ switch (op2type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: return (4); case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (5); case AOP_STK: return (7); + case AOP_SFR: case AOP_HL: return (6); case AOP_IY: /* 4 from ld iy, #... */ @@ -613,27 +730,23 @@ static unsigned char ld_cost(asmop *op1, asmop *op2) { wassert(0); } case AOP_LIT: - case AOP_SIMPLELIT: case AOP_IMMD: wassertl(0, "Trying to assign a value to a literal"); break; default: - printf("ld_cost op1: %d", (int)(op1type)); + printf("ld_cost op1: %d\n", (int)(op1type)); wassert(0); } return (8); // Fallback } -static unsigned char op8_cost(asmop *op2) { +static unsigned char op8_cost(const asmop *op2) { switch (op2->type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: return (1); case AOP_IMMD: case AOP_LIT: - case AOP_SIMPLELIT: return (2); case AOP_STK: return (3); @@ -648,17 +761,15 @@ static unsigned char op8_cost(asmop *op2) { if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) return (3); default: - printf("op8_cost op2: %d", (int)(op2->type)); + printf("op8_cost op2: %d\n", (int)(op2->type)); wassert(0); } return (8); // Fallback } -static unsigned char bit8_cost(asmop *op1) { +static unsigned char bit8_cost(const asmop *op1) { switch (op1->type) { - case AOP_ACC: case AOP_REG: - case AOP_HLREG: case AOP_DUMMY: return (2); case AOP_STK: @@ -669,14 +780,14 @@ static unsigned char bit8_cost(asmop *op1) { case AOP_EXSTK: /* 4 from ld iy, #... */ return (8); default: - printf("bit8_cost op1: %d", (int)(op1->type)); + printf("bit8_cost op1: %d\n", (int)(op1->type)); wassert(0); } return (8); // Fallback } -static unsigned char emit3Cost(enum asminst inst, asmop *op1, int offset1, - asmop *op2, int offset2) { +static unsigned char emit3Cost(enum asminst inst, const asmop *op1, int offset1, + const asmop *op2, int offset2) { if (op2 && offset2 >= op2->size) op2 = ASMOP_ZERO; @@ -684,8 +795,11 @@ static unsigned char emit3Cost(enum asminst inst, asmop *op1, int offset1, case A_CPL: case A_RLA: case A_RLCA: + case A_RRA: case A_RRCA: return (1); + case A_NEG: + return (2); case A_LD: return (ld_cost(op1, op2)); case A_ADD: @@ -758,8 +872,6 @@ static void _emitMove(const char *to, const char *from) { static void _emitMove3(asmop *to, int to_offset, asmop *from, int from_offset) { /* Todo: Longer list of moves that can be optimized out. */ if (to_offset == from_offset) { - if (to->type == AOP_ACC && from->type == AOP_ACC) - return; if (to->type == AOP_REG && from->type == AOP_REG && to->aopu.aop_reg[to_offset] == from->aopu.aop_reg[from_offset]) return; @@ -782,9 +894,6 @@ static const char *aopNames[] = "AOP_CRY", "AOP_IY", "AOP_HL", - "AOP_ACC", - "AOP_HLREG", - "AOP_SIMPLELIT", "AOP_EXSTK", "AOP_PAIRPT", "AOP_DUMMY" @@ -820,7 +929,7 @@ aopDump (const char *plabel, asmop * aop) } #endif -static void _moveA(const char *moveFrom) { _emitMove(ACC_NAME, moveFrom); } +static void _moveA(const char *moveFrom) { _emitMove("a", moveFrom); } /* Load aop into A */ static void _moveA3(asmop *from, int offset) { @@ -839,8 +948,11 @@ static const char *getPairName(asmop *aop) { case L_IDX: return "hl"; break; + case IYL_IDX: + return "iy"; + break; } - } else if (aop->type == AOP_STR || aop->type == AOP_HLREG) { + } else if (aop->type == AOP_STR) { int i; for (i = 0; i < NUM_PAIRS; i++) { if (strcmp(aop->aopu.aop_str[0], _pairs[i].l) == 0) @@ -869,18 +981,6 @@ static bool isUnsplitable(const asmop *aop) { return FALSE; } -static bool isPtrPair(const asmop *aop) { - PAIR_ID pairId = getPairId(aop); - switch (pairId) { - case PAIR_HL: - case PAIR_IY: - case PAIR_IX: - return TRUE; - default: - return FALSE; - } -} - static void spillPair(PAIR_ID pairId) { _G.pairs[pairId].last_type = AOP_INVALID; _G.pairs[pairId].base = NULL; @@ -936,10 +1036,11 @@ static void genMovePairPair(PAIR_ID srcPair, PAIR_ID dstPair) { _push(srcPair); _pop(dstPair); } else { - emit2("ld %s,%s", _pairs[dstPair].l, _pairs[srcPair].l); - emit2("ld %s,%s", _pairs[dstPair].h, _pairs[srcPair].h); + emit2("ld %s, %s", _pairs[dstPair].l, _pairs[srcPair].l); + emit2("ld %s, %s", _pairs[dstPair].h, _pairs[srcPair].h); regalloc_dry_run_cost += 2; } + break; default: wassertl(0, "Tried to move a nonphysical pair"); } @@ -956,14 +1057,14 @@ static asmop *newAsmop(short type) { aop = traceAlloc(&_G.trace.aops, Safe_alloc(sizeof(asmop))); aop->type = type; + memset(aop->regs, -1, 9); return aop; } /*-----------------------------------------------------------------*/ /* aopForSym - for a true symbol */ /*-----------------------------------------------------------------*/ -static asmop *aopForSym(const iCode *ic, symbol *sym, bool result, - bool requires_a) { +static asmop *aopForSym(const iCode *ic, symbol *sym, bool requires_a) { asmop *aop; memmap *space; @@ -984,7 +1085,7 @@ static asmop *aopForSym(const iCode *ic, symbol *sym, bool result, Normally everything is AOP_STK, but for offsets of < -128 or > 127 on the Z80 an extended stack pointer is used. */ - if (!IS_GB && (_G.omitFramePtr || sym->stack < INT8MIN || + if ((_G.omitFramePtr || sym->stack < INT8MIN || sym->stack > (int)(INT8MAX - getSize(sym->type)))) { emitDebug( "; AOP_EXSTK for %s, _G.omitFramePtr %d, sym->stack %d, size %d", @@ -1010,16 +1111,6 @@ static asmop *aopForSym(const iCode *ic, symbol *sym, bool result, if (IN_REGSP(space)) { /*.p.t.20030716 minor restructure to add SFR support to the Z80 */ - if (IS_GB) { - /* if it is in direct space */ - if (!requires_a) { - sym->aop = aop = newAsmop(AOP_SFR); - aop->aopu.aop_dir = sym->rname; - aop->size = getSize(sym->type); - emitDebug("; AOP_SFR for %s", sym->rname); - return aop; - } - } else { /*.p.t.20030716 adding SFR support to the Z80 port */ aop = newAsmop(AOP_SFR); sym->aop = aop; @@ -1027,17 +1118,16 @@ static asmop *aopForSym(const iCode *ic, symbol *sym, bool result, aop->size = getSize(sym->type); aop->paged = FUNC_REGBANK(sym->type); aop->bcInUse = isPairInUse(PAIR_BC, ic); - emitDebug(";Z80 AOP_SFR for %s banked:%d bc:%d", sym->rname, - FUNC_REGBANK(sym->type), aop->bcInUse); + /* emitDebug (";Z80 AOP_SFR for %s banked:%d bc:%d", sym->rname, + * FUNC_REGBANK (sym->type), aop->bcInUse); */ return (aop); - } } /* only remaining is far space */ /* in which case DPTR gets the address */ - if (IS_GB || IY_RESERVED) { - emitDebug("; AOP_HL for %s", sym->rname); + if (IY_RESERVED) { + /* emitDebug ("; AOP_HL for %s", sym->rname); */ sym->aop = aop = newAsmop(AOP_HL); } else sym->aop = aop = newAsmop(AOP_IY); @@ -1179,6 +1269,12 @@ static bool sameRegs(asmop *aop1, asmop *aop2) { if (aop1 == aop2) return TRUE; + if (!regalloc_dry_run && // Todo: Check if always enabling this even for dry + // runs tends to result in better code. + (aop1->type == AOP_STK && aop2->type == AOP_STK || + aop1->type == AOP_EXSTK && aop2->type == AOP_EXSTK)) + return (aop1->aopu.aop_stk == aop2->aopu.aop_stk); + if (aop1->type != AOP_REG || aop2->type != AOP_REG) return FALSE; @@ -1204,7 +1300,9 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { return; /* if this a literal */ - if (IS_OP_LITERAL(op)) { + if (IS_OP_LITERAL(op)) /* TODO: && !op->isaddr, handle address literals in a + sane way */ + { op->aop = aop = newAsmop(AOP_LIT); aop->aopu.aop_lit = OP_VALUE(op); aop->size = getSize(operandType(op)); @@ -1230,7 +1328,7 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { /* if this is a true symbol */ if (IS_TRUE_SYMOP(op)) { - op->aop = aopForSym(ic, OP_SYMBOL(op), result, requires_a); + op->aop = aopForSym(ic, OP_SYMBOL(op), requires_a); return; } @@ -1260,36 +1358,28 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { aop = op->aop = sym->aop = newAsmop(AOP_STR); aop->size = getSize(sym->type); for (i = 0; i < 4; i++) - aop->aopu.aop_str[i] = _fReturn[i]; + aop->aopu.aop_str[i] = ASMOP_RETURN->aopu.aop_reg[i]->name; return; } if (sym->accuse) { - if (sym->accuse == ACCUSE_A) { - aop = op->aop = sym->aop = newAsmop(AOP_ACC); + if (sym->accuse == + ACCUSE_A) /* For compability with old register allocator only */ + { + sym->aop = op->aop = aop = newAsmop(AOP_REG); aop->size = getSize(sym->type); wassertl(aop->size == 1, "Internal error: Caching in A, but too big to fit in A"); - - aop->aopu.aop_str[0] = _pairs[PAIR_AF].h; - } -#if 0 // HL is not handled as any other register pair. - else if (sym->accuse == ACCUSE_SCRATCH) - { - aop = op->aop = sym->aop = newAsmop (AOP_HLREG); - aop->size = getSize (sym->type); - wassertl (aop->size <= 2, "Internal error: Caching in HL, but too big to fit in HL"); - aop->aopu.aop_str[0] = _pairs[PAIR_HL].l; - aop->aopu.aop_str[1] = _pairs[PAIR_HL].h; - } -#endif - else if (sym->accuse == ACCUSE_IY) { - aop = op->aop = sym->aop = newAsmop(AOP_HLREG); + aop->aopu.aop_reg[0] = regsZ80 + A_IDX; + } else if (sym->accuse == ACCUSE_IY) /* For compability with old register + allocator only */ + { + sym->aop = op->aop = aop = newAsmop(AOP_REG); aop->size = getSize(sym->type); wassertl(aop->size <= 2, "Internal error: Caching in IY, but too big to fit in IY"); - aop->aopu.aop_str[0] = _pairs[PAIR_IY].l; - aop->aopu.aop_str[1] = _pairs[PAIR_IY].h; + aop->aopu.aop_reg[0] = regsZ80 + IYL_IDX; + aop->aopu.aop_reg[0] = regsZ80 + IYH_IDX; } else { wassertl(0, "Marked as being allocated into A or IY but is actually in " "neither"); @@ -1312,7 +1402,7 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { } /* On stack. */ - if (sym->usl.spillLoc) { + if (sym->isspilt && sym->usl.spillLoc) { asmop *oldAsmOp = NULL; if (getSize(sym->type) != getSize(sym->usl.spillLoc->type)) { @@ -1320,8 +1410,7 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { oldAsmOp = sym->usl.spillLoc->aop; sym->usl.spillLoc->aop = NULL; } - sym->aop = op->aop = aop = - aopForSym(ic, sym->usl.spillLoc, result, requires_a); + sym->aop = op->aop = aop = aopForSym(ic, sym->usl.spillLoc, requires_a); if (getSize(sym->type) != getSize(sym->usl.spillLoc->type)) { /* Don't reuse the new aop, go with the last one */ sym->usl.spillLoc->aop = oldAsmOp; @@ -1344,6 +1433,7 @@ static void aopOp(operand *op, const iCode *ic, bool result, bool requires_a) { if (!sym->regs[i]) fprintf(stderr, "Symbol %s at ic %d.\n", sym->name, ic->key); aop->aopu.aop_reg[i] = sym->regs[i]; + aop->regs[sym->regs[i]->rIdx] = i; } } @@ -1366,7 +1456,7 @@ static void freeAsmop(operand *op, asmop *aaop) { aop->freed = 1; - if (aop->type == AOP_PAIRPTR && !IS_GB && aop->aopu.aop_pairId == PAIR_DE) { + if (aop->type == AOP_PAIRPTR && aop->aopu.aop_pairId == PAIR_DE) { _pop(aop->aopu.aop_pairId); } @@ -1429,18 +1519,12 @@ static const char *aopGetLitWordLong(const asmop *aop, int offset, /* if it is a float then it gets tricky */ /* otherwise it is fairly simple */ if (!IS_FLOAT(val->type)) { - unsigned long v = ulFromVal(val); + unsigned long long v = ullFromVal(val); - if (offset == 2) { - v >>= 16; - } else if (offset == 0) { - // OK - } else { - wassertl(0, "Encountered an invalid offset while fetching a literal"); - } + v >>= (offset * 8); dbuf_tprintf(&dbuf, with_hash ? "!immedword" : "!constword", - v & 0xfffful); + (unsigned long)(v & 0xffffull)); } else { union { float f; @@ -1460,15 +1544,12 @@ static const char *aopGetLitWordLong(const asmop *aop, int offset, } } break; - case AOP_HLREG: case AOP_REG: case AOP_STK: case AOP_DIR: case AOP_SFR: case AOP_STR: case AOP_CRY: - case AOP_ACC: - case AOP_SIMPLELIT: case AOP_EXSTK: case AOP_PAIRPTR: case AOP_DUMMY: @@ -1476,6 +1557,7 @@ static const char *aopGetLitWordLong(const asmop *aop, int offset, default: dbuf_destroy(&dbuf); + fprintf(stderr, "aop->type: %d\n", aop->type); wassertl(0, "aopGetLitWordLong got unsupported aop->type"); exit(0); } @@ -1516,10 +1598,9 @@ static bool requiresHL(const asmop *aop) { return FALSE; case AOP_HL: case AOP_EXSTK: - case AOP_HLREG: return TRUE; case AOP_STK: - return (IS_GB || _G.omitFramePtr); + return (_G.omitFramePtr); case AOP_REG: { int i; for (i = 0; i < aop->size; i++) { @@ -1536,6 +1617,43 @@ static bool requiresHL(const asmop *aop) { } } +/*----------------------------------------------------------*/ +/* strtoul_z80: a wrapper to strtoul, which can also handle */ +/* hex numbers with a $ prefix. */ +/*----------------------------------------------------------*/ +static unsigned long int strtoul_z80asm(const char *nptr, char **endptr, + int base) { + char *p = NULL; + int i, flag = 0, len; + unsigned long ret; + + if (nptr != NULL && (p = malloc((len = strlen(nptr)) + 1 + 1)) != NULL) { + memset(p, 0, len + 2); + for (i = 0; i < len; i++) { + if (!flag) + if (isspace(nptr[i])) + p[i] = nptr[i]; + else if (nptr[i] == '$') { + p[i] = '0'; + p[i + 1] = 'x'; + flag = 1; + } else + break; + else + p[i + 1] = nptr[i]; + } + } + + if (flag) + ret = strtoul(p, endptr, base); + else + ret = strtoul(nptr, endptr, base); + + if (p) + free(p); + return ret; +} + static void fetchLitPair(PAIR_ID pairId, asmop *left, int offset) { const char *pair = _pairs[pairId].name; char *l = Safe_strdup(aopGetLitWordLong(left, offset, FALSE)); @@ -1547,7 +1665,7 @@ static void fetchLitPair(PAIR_ID pairId, asmop *left, int offset) { emitDebug(";fetchLitPair"); if (isPtr(pair)) { - if (pairId == PAIR_HL) { + if (pairId == PAIR_HL || pairId == PAIR_IY) { if (pairId == PAIR_HL && base[0] == '0') // Ugly workaround { unsigned int tmpoffset; @@ -1567,6 +1685,8 @@ static void fetchLitPair(PAIR_ID pairId, asmop *left, int offset) { adjustPair(pair, &_G.pairs[pairId].offset, offset); goto adjusted; } + if (pairId == PAIR_IY && offset == _G.pairs[pairId].offset) + goto adjusted; } } } @@ -1577,14 +1697,21 @@ static void fetchLitPair(PAIR_ID pairId, asmop *left, int offset) { _G.pairs[pairId].offset == 0) { unsigned new_low, new_high, old_low, old_high; unsigned long v_new = ulFromVal(left->aopu.aop_lit); - unsigned long v_old = strtoul(_G.pairs[pairId].base, NULL, 0); + unsigned long v_old = strtoul_z80asm(_G.pairs[pairId].base, NULL, 0); new_low = (v_new >> 0) & 0xff; new_high = (v_new >> 8) & 0xff; old_low = (v_old >> 0) & 0xff; old_high = (v_old >> 8) & 0xff; + if (new_high == old_high && new_low == old_high) { + emit3_o(A_LD, ASMOP_L, 0, ASMOP_H, 0); + goto adjusted; + } else if (new_low == old_low && new_high == old_low) { + emit3_o(A_LD, ASMOP_H, 0, ASMOP_L, 0); + goto adjusted; + } /* Change lower byte only. */ - if (new_high == old_high) { + else if (new_high == old_high) { emit3_o(A_LD, ASMOP_L, 0, left, 0); goto adjusted; } @@ -1599,9 +1726,10 @@ static void fetchLitPair(PAIR_ID pairId, asmop *left, int offset) { _G.pairs[pairId].base = traceAlloc(&_G.trace.aops, Safe_strdup(base)); _G.pairs[pairId].offset = offset; } + /* Both a lit on the right and a true symbol on the left */ - emit2("ld %s,!hashedstr", pair, l); - regalloc_dry_run_cost += (pairId == PAIR_IX || pairId == PAIR_IY) ? 4 : 3; + emit2("ld %s, !hashedstr", pair, l); + regalloc_dry_run_cost += (pairId == PAIR_IX || pairId == PAIR_IY) ? 4 : 3; Safe_free(base_str); Safe_free(l); return; @@ -1621,7 +1749,7 @@ static PAIR_ID makeFreePairId(const iCode *ic, bool *pisUsed) { if (!bitVectBitValue(ic->rMask, B_IDX) && !bitVectBitValue(ic->rMask, C_IDX)) { return PAIR_BC; - } else if (!IS_GB && !bitVectBitValue(ic->rMask, D_IDX) && + } else if (!bitVectBitValue(ic->rMask, D_IDX) && !bitVectBitValue(ic->rMask, E_IDX)) { return PAIR_DE; } else { @@ -1643,32 +1771,35 @@ static void fetchPairLong(PAIR_ID pairId, asmop *aop, const iCode *ic, if (isLitWord(aop)) fetchLitPair(pairId, aop, offset); else { - if (getPairId(aop) == pairId) { + if (getPairId_o(aop, offset) == pairId) { /* Do nothing */ + } else if (IS_EZ80_Z80 && aop->size - offset >= 2 && aop->type == AOP_STK) { + int fp_offset = aop->aopu.aop_stk + offset + + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + emit2("ld %s, %d (ix)", _pairs[pairId].name, fp_offset); + regalloc_dry_run_cost += 3; } /* Getting the parameter by a pop / push sequence is cheaper when we have a free pair (except for the Rabbit, which has an even cheaper sp-relative load). Stack allocation can change after register allocation, so assume this optimization is not possible for the allocator's cost function (unless the stack location is for a parameter). */ - else if (!IS_RAB && aop->size - offset >= 2 && + else if (aop->size - offset >= 2 && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && (!regalloc_dry_run || aop->aopu.aop_stk > 0) && (aop->aopu.aop_stk + offset + _G.stack.offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0) + _G.stack.pushed) == 2 && - ic && - (pairId != PAIR_BC && isPairDead(PAIR_BC, ic) || - pairId != PAIR_DE && isPairDead(PAIR_DE, ic))) { - PAIR_ID extrapair = - (pairId != PAIR_BC && isPairDead(PAIR_BC, ic)) ? PAIR_BC : PAIR_DE; + ic && getFreePairId(ic) != PAIR_INVALID && + getFreePairId(ic) != pairId) { + PAIR_ID extrapair = getFreePairId(ic); _pop(extrapair); _pop(pairId); _push(pairId); _push(extrapair); } /* Todo: Use even cheaper ex hl, (sp) and ex iy, (sp) when possible. */ - else if ((!IS_RAB || pairId == PAIR_BC || pairId == PAIR_DE) && + else if ((pairId == PAIR_BC || pairId == PAIR_DE) && aop->size - offset >= 2 && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && (!regalloc_dry_run || aop->aopu.aop_stk > 0) && @@ -1677,60 +1808,57 @@ static void fetchPairLong(PAIR_ID pairId, asmop *aop, const iCode *ic, _G.stack.pushed) == 0) { _pop(pairId); _push(pairId); + } else if ((aop->type == AOP_IY || aop->type == AOP_HL) && + !(pairId == PAIR_IY && aop->size < 2)) { + /* Instead of fetching relative to IY, just grab directly + from the address IY refers to */ + emit2("ld %s, (%s)", _pairs[pairId].name, + aopGetLitWordLong(aop, offset, FALSE)); + regalloc_dry_run_cost += (pairId == PAIR_HL ? 3 : 4); + + if (aop->size < 2) { + emit2("ld %s, !zero", _pairs[pairId].h); + regalloc_dry_run_cost += 2; + } } /* we need to get it byte by byte */ - else if (pairId == PAIR_HL && - (IS_GB || (IY_RESERVED && aop->type == AOP_HL)) && + else if (pairId == PAIR_HL && (IY_RESERVED) && + (aop->type == AOP_HL || aop->type == AOP_EXSTK) && requiresHL(aop)) { if (!regalloc_dry_run) // TODO: Fix this to get correct cost! aopGet(aop, offset, FALSE); switch (aop->size - offset) { case 1: - emit2("ld l,!*hl"); - emit2("ld h,!immedbyte", 0); + emit2("ld l, !*hl"); + emit2("ld h, !immedbyte", 0); regalloc_dry_run_cost += 3; break; default: wassertl(aop->size - offset > 1, "Attempted to fetch no data into HL"); - if (IS_RAB || IS_TLCS90) { - emit2("ld hl, 0 (hl)"); - regalloc_dry_run_cost += 3; +if (IS_EZ80_Z80) { + emit2("ld hl, (hl)"); + regalloc_dry_run_cost += 2; } else { - emit2("ld a,!*hl"); + if (ic && bitVectBitValue(ic->rMask, A_IDX)) + _push(PAIR_AF); + + emit2("ld a, !*hl"); emit2("inc hl"); - emit2("ld h,!*hl"); - emit2("ld l,a"); + emit2("ld h, !*hl"); + emit2("ld l, a"); regalloc_dry_run_cost += 4; + + if (ic && bitVectBitValue(ic->rMask, A_IDX)) + _pop(PAIR_AF); } break; } - } else if (!IS_GB && aop->type == AOP_IY && - !(pairId == PAIR_IY && aop->size < 2)) { - /* Instead of fetching relative to IY, just grab directly - from the address IY refers to */ - emit2("ld %s,(%s)", _pairs[pairId].name, - aopGetLitWordLong(aop, offset, FALSE)); - regalloc_dry_run_cost += (pairId == PAIR_HL ? 3 : 4); - - if (aop->size < 2) { - emit2("ld %s,!zero", _pairs[pairId].h); - regalloc_dry_run_cost += 2; - } } else if (pairId == PAIR_IY) { /* The Rabbit has the ld iy, n (sp) instruction. */ - int fp_offset = aop->aopu.aop_stk + offset + _G.stack.offset + + int fp_offset = aop->aopu.aop_stk + offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - int sp_offset = fp_offset + _G.stack.pushed; - if ((IS_RAB || IS_TLCS90) && - (aop->type == AOP_STK || aop->type == AOP_EXSTK) && - abs(sp_offset) <= 127) { - emit2("ld iy, (sp + %d)", sp_offset); - regalloc_dry_run_cost += 3; - } else if (isPair(aop) && (IS_RAB || IS_TLCS90) && - getPairId(aop) == PAIR_HL) { - emit2("ld iy, hl"); - regalloc_dry_run_cost += (1 + IS_RAB); - } else if (isPair(aop)) { + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; +if (isPair(aop)) { emit2("push %s", _pairs[getPairId(aop)].name); emit2("pop iy"); regalloc_dry_run_cost += 3; @@ -1741,19 +1869,14 @@ static void fetchPairLong(PAIR_ID pairId, asmop *aop, const iCode *ic, _push(id); /* Can't load into parts, so load into HL then exchange. */ if (!regalloc_dry_run) { - emit2("ld %s,%s", _pairs[id].l, aopGet(aop, offset, FALSE)); - emit2("ld %s,%s", _pairs[id].h, aopGet(aop, offset + 1, FALSE)); + emit2("ld %s, %s", _pairs[id].l, aopGet(aop, offset, FALSE)); + emit2("ld %s, %s", _pairs[id].h, aopGet(aop, offset + 1, FALSE)); } regalloc_dry_run_cost += ld_cost(ASMOP_L, aop) + ld_cost(ASMOP_H, aop); - if ((IS_RAB || IS_TLCS90) && id == PAIR_HL) { - emit2("ld iy, hl"); - regalloc_dry_run_cost += (1 + IS_RAB); - } else { emit2("push %s", _pairs[id].name); emit2("pop iy"); regalloc_dry_run_cost += 3; - } if (isUsed) _pop(id); } @@ -1764,32 +1887,12 @@ static void fetchPairLong(PAIR_ID pairId, asmop *aop, const iCode *ic, (pairId == PAIR_IY ? 2 : 1) + (getPairId(aop) == PAIR_IY ? 2 : 1); } else { /* The Rabbit has the ld hl, n (sp) and ld hl, n (ix) instructions. */ - int fp_offset = aop->aopu.aop_stk + offset + _G.stack.offset + + int fp_offset = aop->aopu.aop_stk + offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - int sp_offset = fp_offset + _G.stack.pushed; - if ((IS_RAB || IS_TLCS90) && aop->size - offset >= 2 && - (aop->type == AOP_STK || aop->type == AOP_EXSTK) && - (pairId == PAIR_HL || pairId == PAIR_IY || pairId == PAIR_DE) && - (abs(fp_offset) <= 127 && pairId == PAIR_HL && aop->type == AOP_STK || - abs(sp_offset) <= 127)) { - if (pairId == PAIR_DE) { - emit2("ex de, hl"); - regalloc_dry_run_cost += 1; - } - if (abs(sp_offset) <= 127) - emit2("ld %s, (sp + %d)", pairId == PAIR_IY ? "iy" : "hl", - sp_offset); /* Fetch relative to stack pointer. */ - else - emit2("ld hl, (ix + %d)", - fp_offset); /* Fetch relative to frame pointer. */ - regalloc_dry_run_cost += (pairId == PAIR_IY ? 3 : 2); - if (pairId == PAIR_DE) { - emit2("ex de, hl"); - regalloc_dry_run_cost += 1; - } - } + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + /* Operand resides (partially) in the pair */ - else if (!regalloc_dry_run && + if (!regalloc_dry_run && !strcmp( aopGet(aop, offset + 1, FALSE), _pairs[pairId].l)) // aopGet (aop, offset + 1, FALSE) is @@ -1797,72 +1900,25 @@ static void fetchPairLong(PAIR_ID pairId, asmop *aop, const iCode *ic, // exact cost, and results in redundant // code being generated. Todo: Exact cost { - _moveA3(aop, offset + 1); + _moveA3(aop, offset); if (!regalloc_dry_run) - emit2("ld %s,%s", _pairs[pairId].l, aopGet(aop, offset, FALSE)); + emit2("ld %s, %s", _pairs[pairId].h, aopGet(aop, offset + 1, FALSE)); regalloc_dry_run_cost += ld_cost(ASMOP_A, aop); - emit2("ld %s,a", _pairs[pairId].h); + emit2("ld %s, a", _pairs[pairId].l); regalloc_dry_run_cost += 1; } /* The Rabbit's cast to bool is a cheap way of zeroing h (similar to xor a, a for a for the Z80). */ - else if (pairId == PAIR_HL && IS_RAB && aop->size - offset == 1 && - !(aop->type == AOP_REG && - (aop->aopu.aop_reg[offset]->rIdx == L_IDX || - aop->aopu.aop_reg[offset]->rIdx == H_IDX))) { - emit2("bool hl"); - regalloc_dry_run_cost++; - if (!regalloc_dry_run) - emit2("ld %s,%s", _pairs[pairId].l, aopGet(aop, offset, FALSE)); - regalloc_dry_run_cost += ld_cost(ASMOP_L, aop); - } else if (pairId == PAIR_HL && aop->type == AOP_REG && - aop->size - offset >= 2 && - aop->aopu.aop_reg[offset]->rIdx != H_IDX && - aop->aopu.aop_reg[offset + 1]->rIdx != L_IDX) { - if (aop->aopu.aop_reg[offset + 0]->rIdx != L_IDX) { - if (!regalloc_dry_run) - emit2("ld l, %s", aopGet(aop, offset + 0, FALSE)); - regalloc_dry_run_cost++; - } - if (aop->aopu.aop_reg[offset + 1]->rIdx != H_IDX) { - if (!regalloc_dry_run) - emit2("ld h, %s", aopGet(aop, offset + 1, FALSE)); - regalloc_dry_run_cost++; - } - } else if (pairId == PAIR_DE && aop->type == AOP_REG && - aop->size - offset >= 2 && - aop->aopu.aop_reg[offset]->rIdx != D_IDX && - aop->aopu.aop_reg[offset + 1]->rIdx != E_IDX) { - if (aop->aopu.aop_reg[offset + 0]->rIdx != E_IDX) { - if (!regalloc_dry_run) - emit2("ld e, %s", aopGet(aop, offset + 0, FALSE)); - regalloc_dry_run_cost++; - } - if (aop->aopu.aop_reg[offset + 1]->rIdx != D_IDX) { + if (!aopInReg(aop, offset, _pairs[pairId].l_idx)) { if (!regalloc_dry_run) - emit2("ld d, %s", aopGet(aop, offset + 1, FALSE)); - regalloc_dry_run_cost++; - } - } else if (pairId == PAIR_BC && aop->type == AOP_REG && - aop->size - offset >= 2 && - aop->aopu.aop_reg[offset]->rIdx != B_IDX && - aop->aopu.aop_reg[offset + 1]->rIdx != C_IDX) { - if (aop->aopu.aop_reg[offset + 0]->rIdx != C_IDX) { - if (!regalloc_dry_run) - emit2("ld c, %s", aopGet(aop, offset + 0, FALSE)); - regalloc_dry_run_cost++; + emit2("ld %s, %s", _pairs[pairId].l, aopGet(aop, offset, FALSE)); + regalloc_dry_run_cost += ld_cost(ASMOP_L, aop); } - if (aop->aopu.aop_reg[offset + 1]->rIdx != B_IDX) { + if (!aopInReg(aop, offset + 1, _pairs[pairId].h_idx)) { if (!regalloc_dry_run) - emit2("ld b, %s", aopGet(aop, offset + 1, FALSE)); - regalloc_dry_run_cost++; - } - } else { - if (!regalloc_dry_run) { - emit2("ld %s,%s", _pairs[pairId].l, aopGet(aop, offset, FALSE)); - emit2("ld %s,%s", _pairs[pairId].h, aopGet(aop, offset + 1, FALSE)); - } - regalloc_dry_run_cost += ld_cost(ASMOP_L, aop) * 2; + emit2("ld %s, %s", _pairs[pairId].h, + aopGet(aop, offset + 1, FALSE)); + regalloc_dry_run_cost += ld_cost(ASMOP_H, aop); } } /* PENDING: check? */ @@ -1875,28 +1931,100 @@ static void fetchPair(PAIR_ID pairId, asmop *aop) { } static void setupPairFromSP(PAIR_ID id, int offset) { - wassertl(id == PAIR_HL, "Setup relative to SP only implemented for HL"); + wassertl(id == PAIR_HL || id == PAIR_DE || id == PAIR_IY, + "Setup relative to SP only implemented for HL, DE, IY"); if (_G.preserveCarry) { _push(PAIR_AF); + regalloc_dry_run_cost++; offset += 2; } - if (offset < INT8MIN || offset > INT8MAX) { - emit2("ld hl,!immedword", offset); - emit2("add hl,sp"); - regalloc_dry_run_cost += 4; + if (id == PAIR_DE ) // TODO: Could hl be in use for gbz80, so it + // needs to be saved and restored? + { + emit2("ex de, hl"); + regalloc_dry_run_cost++; + } + + if (offset < INT8MIN || offset > INT8MAX || id == PAIR_IY) { + struct dbuf_s dbuf; + PAIR_ID lid = (id == PAIR_DE) ? PAIR_HL : id; + dbuf_init(&dbuf, sizeof(int) * 3 + 1); + dbuf_printf(&dbuf, "%d", offset); + emit2("ld %s, !hashedstr", _pairs[lid].name, dbuf_c_str(&dbuf)); + dbuf_destroy(&dbuf); + emit2("add %s, sp", _pairs[lid].name); + regalloc_dry_run_cost += 4 + (id == PAIR_IY) * 2; } else { + wassert(id == PAIR_DE || id == PAIR_HL); emit2("!ldahlsp", offset); - regalloc_dry_run_cost += 3; + regalloc_dry_run_cost += 4; + } + + if (id == PAIR_DE) { + emit2("ex de, hl"); + regalloc_dry_run_cost++; + } else if (id == PAIR_DE) { + genMovePairPair(PAIR_HL, PAIR_DE); + spillPair(PAIR_HL); } if (_G.preserveCarry) { _pop(PAIR_AF); + regalloc_dry_run_cost++; offset -= 2; } } +static void shiftIntoPair(PAIR_ID id, asmop *aop); + +/*-----------------------------------------------------------------*/ +/* pointPairToAop() make a register pair point to a byte of an aop */ +/*-----------------------------------------------------------------*/ +static void pointPairToAop(PAIR_ID pairId, const asmop *aop, int offset) { + switch (aop->type) { + case AOP_EXSTK: + + case AOP_STK:; + int abso = aop->aopu.aop_stk + offset + _G.stack.offset + + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + + if ((_G.pairs[pairId].last_type == AOP_STK || + _G.pairs[pairId].last_type == AOP_EXSTK) && + abs(_G.pairs[pairId].offset - abso) < 3) + adjustPair(_pairs[pairId].name, &_G.pairs[pairId].offset, abso); + else + setupPairFromSP(pairId, abso + _G.stack.pushed); + + _G.pairs[pairId].offset = abso; + + break; + + case AOP_HL: // Legacy. + fetchLitPair(pairId, (asmop *)aop, offset); + _G.pairs[pairId].offset = offset; + break; + + case AOP_PAIRPTR: + wassert(!offset); + + shiftIntoPair( + pairId, + (asmop *)aop); // Legacy. Todo eliminate uses of shiftIntoPair() ? + + break; + + default: + wassertl(0, "Unsupported aop type for pointPairToAop()"); + } + + _G.pairs[pairId].last_type = aop->type; +} + +// Weird function. Sometimes offset is used, sometimes not. +// Callers rely on that behaviour. Uses of this should be replaced +// by pointPairToAop() above after the 3.7.0 release. static void setupPair(PAIR_ID pairId, asmop *aop, int offset) { switch (aop->type) { case AOP_IY: @@ -1913,7 +2041,6 @@ static void setupPair(PAIR_ID pairId, asmop *aop, int offset) { break; case AOP_EXSTK: - wassertl(!IS_GB, "The GBZ80 doesn't have an extended stack"); wassertl(pairId == PAIR_IY || pairId == PAIR_HL, "The Z80 extended stack must be in IY or HL"); @@ -1924,9 +2051,9 @@ static void setupPair(PAIR_ID pairId, asmop *aop, int offset) { offset += _G.stack.param_offset; if (_G.pairs[pairId].last_type == aop->type && - _G.pairs[pairId].offset == offset) { - /* Already setup */ - } else { + abs(_G.pairs[pairId].offset - offset) <= 3) + adjustPair(_pairs[pairId].name, &_G.pairs[pairId].offset, offset); + else { struct dbuf_s dbuf; /* PENDING: Do this better. */ @@ -1934,9 +2061,9 @@ static void setupPair(PAIR_ID pairId, asmop *aop, int offset) { _push(PAIR_AF); dbuf_init(&dbuf, 128); dbuf_printf(&dbuf, "%d", offset + _G.stack.pushed); - emit2("ld %s,!hashedstr", _pairs[pairId].name, dbuf_c_str(&dbuf)); + emit2("ld %s, !hashedstr", _pairs[pairId].name, dbuf_c_str(&dbuf)); dbuf_destroy(&dbuf); - emit2("add %s,sp", _pairs[pairId].name); + emit2("add %s, sp", _pairs[pairId].name); _G.pairs[pairId].last_type = aop->type; _G.pairs[pairId].offset = offset; if (_G.preserveCarry) @@ -1985,7 +2112,7 @@ static void emitLabelSpill(symbol *tlbl) { static const char *aopGet(asmop *aop, int offset, bool bit16) { static struct dbuf_s dbuf = {0}; - wassert(!regalloc_dry_run); + wassert_bt(!regalloc_dry_run); if (dbuf_is_initialized(&dbuf)) { /* reuse the dynamically allocated buffer */ @@ -2009,11 +2136,13 @@ static const char *aopGet(asmop *aop, int offset, bool bit16) { case AOP_IMMD: /* PENDING: re-target */ if (bit16) - dbuf_tprintf(&dbuf, "!immedwords", aop->aopu.aop_immd); + dbuf_tprintf(&dbuf, "!immedword", aop->aopu.aop_immd); else { switch (offset) { case 2: - dbuf_tprintf(&dbuf, "!bankimmeds", aop->aopu.aop_immd); + // dbuf_tprintf (&dbuf, "!bankimmeds", aop->aopu.aop_immd); Bank + // support not fully implemented yet. + dbuf_tprintf(&dbuf, "#0x00"); break; case 1: @@ -2025,46 +2154,31 @@ static const char *aopGet(asmop *aop, int offset, bool bit16) { break; default: - wassertl(0, "Fetching from beyond the limits of an immediate value."); + dbuf_tprintf(&dbuf, "#0x00"); } } break; case AOP_DIR: - wassert(IS_GB); - emit2("ld a,(%s+%d)", aop->aopu.aop_dir, offset); - regalloc_dry_run_cost += 3; - dbuf_append_char(&dbuf, 'a'); + wassert(false); break; case AOP_SFR: - if (IS_GB) { - emit2("ldh a,(%s+%d)", aop->aopu.aop_dir, offset); - regalloc_dry_run_cost += 2; - dbuf_append_char(&dbuf, 'a'); - } else if (IS_RAB) { - emit2("ioi"); - emit2("ld a,(%s)", aop->aopu.aop_dir); - emit2("nop"); /* Workaround for Rabbit 2000 hardware bug. see TN302 for - details. */ - dbuf_append_char(&dbuf, 'a'); - } else { /*.p.t.20030716 handling for i/o port read access for Z80 */ if (aop->paged) { /* banked mode */ /* reg A goes to address bits 15-8 during "in a,(x)" instruction */ - emit2("ld a,!msbimmeds", aop->aopu.aop_dir); - emit2("in a,(!lsbimmeds)", aop->aopu.aop_dir); + emit2("ld a, !msbimmeds", aop->aopu.aop_dir); + emit2("in a, (!lsbimmeds)", aop->aopu.aop_dir); } else if (z80_opts.port_mode == 180) { /* z180 in0/out0 mode */ - emit2("in0 a,(%s)", aop->aopu.aop_dir); + emit2("in0 a, (%s)", aop->aopu.aop_dir); } else { /* 8 bit mode */ - emit2("in a,(%s)", aop->aopu.aop_dir); + emit2("in a, (%s)", aop->aopu.aop_dir); } dbuf_append_char(&dbuf, 'a'); - } break; case AOP_REG: @@ -2077,20 +2191,20 @@ static const char *aopGet(asmop *aop, int offset, bool bit16) { break; case AOP_IY: - wassert(!IS_GB); setupPair(PAIR_IY, aop, offset); dbuf_tprintf(&dbuf, "!*iyx", offset); break; case AOP_EXSTK: - wassert(!IS_GB); - setupPair(PAIR_IY, aop, offset); - dbuf_tprintf(&dbuf, "!*iyx", offset); - break; + if (!IY_RESERVED) { + setupPair(PAIR_IY, aop, offset); + dbuf_tprintf(&dbuf, "!*iyx", offset); + break; + } case AOP_STK: - if (IS_GB) { - setupPair(PAIR_HL, aop, offset); + if (aop->type == AOP_EXSTK) { + pointPairToAop(PAIR_HL, aop, offset); dbuf_tprintf(&dbuf, "!*hl"); } else if (_G.omitFramePtr) { if (aop->aopu.aop_stk >= 0) @@ -2108,32 +2222,10 @@ static const char *aopGet(asmop *aop, int offset, bool bit16) { wassertl(0, "Tried to fetch from a bit variable"); break; - case AOP_ACC: - if (!offset) { - dbuf_append_char(&dbuf, 'a'); - } else { - dbuf_tprintf(&dbuf, "!zero"); - } - break; - - case AOP_HLREG: - dbuf_append_str(&dbuf, aop->aopu.aop_str[offset]); - break; - case AOP_LIT: dbuf_append_str(&dbuf, aopLiteral(aop->aopu.aop_lit, offset)); break; - case AOP_SIMPLELIT: { - unsigned long v = aop->aopu.aop_simplelit; - - if (offset >= sizeof(v)) - v = 0; - else - v >>= (offset * 8); - dbuf_tprintf(&dbuf, "!immedbyte", (unsigned int)v & 0xff); - } break; - case AOP_STR: aop->coff = offset; dbuf_append_str(&dbuf, aop->aopu.aop_str[offset]); @@ -2151,7 +2243,8 @@ static const char *aopGet(asmop *aop, int offset, bool bit16) { default: dbuf_destroy(&dbuf); - wassertl(0, "aopget got unsupported aop->type"); + fprintf(stderr, "aop->type: %d\n", aop->type); + wassertl(0, "aopGet got unsupported aop->type"); exit(0); } } @@ -2187,10 +2280,9 @@ static bool canAssignToPtr(const char *s) { } static bool canAssignToPtr3(const asmop *aop) { - if (aop->type == AOP_ACC || aop->type == AOP_REG || aop->type == AOP_HLREG) + if (aop->type == AOP_REG) return (TRUE); - if (aop->type == AOP_IMMD || aop->type == AOP_LIT || - aop->type == AOP_SIMPLELIT) + if (aop->type == AOP_IMMD || aop->type == AOP_LIT) return (TRUE); return (FALSE); } @@ -2204,8 +2296,8 @@ static void aopPut(asmop *aop, const char *s, int offset) { wassert(!regalloc_dry_run); if (aop->size && offset > (aop->size - 1)) { - werror(E_INTERNAL_ERROR, __FILE__, __LINE__, - "aopPut got offset > aop->size"); + werror_bt(E_INTERNAL_ERROR, __FILE__, __LINE__, + "aopPut got offset > aop->size"); exit(0); } @@ -2223,30 +2315,10 @@ static void aopPut(asmop *aop, const char *s, int offset) { case AOP_DIR: /* Direct. Hmmm. */ - wassert(IS_GB); - if (strcmp(s, "a")) - emit2("ld a,%s", s); - emit2("ld (%s+%d),a", aop->aopu.aop_dir, offset); + wassert(false); break; case AOP_SFR: - if (IS_GB) { - // wassert (IS_GB); - if (strcmp(s, "a")) - emit2("ld a,%s", s); - emit2("ldh (%s+%d),a", aop->aopu.aop_dir, offset); - } else if (IS_RAB) { - if (strcmp(s, "a")) - emit2("ld a,%s", s); - - /* LM 20110928: Need to fix to emit either "ioi" or "ioe" - * (for internal vs. external I/O space - */ - emit2("ioi"); - emit2("ld (%s),a", aop->aopu.aop_dir); - emit2("nop"); /* Workaround for Rabbit 2000 hardware bug. see TN302 for - details. */ - } else { /*.p.t.20030716 handling for i/o port read access for Z80 */ if (aop->paged) { /* banked mode */ @@ -2255,11 +2327,11 @@ static void aopPut(asmop *aop, const char *s, int offset) { if (strlen(s) != 1 || (s[0] != 'a' && s[0] != 'd' && s[0] != 'e' && s[0] != 'h' && s[0] != 'l')) { - emit2("ld a,%s", s); + emit2("ld a, %s", s); s = "a"; } - emit2("ld bc,#%s", aop->aopu.aop_dir); + emit2("ld bc, !hashedstr", aop->aopu.aop_dir); emit2("out (c),%s", s); if (aop->bcInUse) @@ -2268,33 +2340,34 @@ static void aopPut(asmop *aop, const char *s, int offset) { spillPair(PAIR_BC); } else if (z80_opts.port_mode == 180) { /* z180 in0/out0 mode */ - emit2("ld a,%s", s); - emit2("out0 (%s),a", aop->aopu.aop_dir); + emit2("ld a, %s", s); + emit2("out0 (%s), a", aop->aopu.aop_dir); } else { /* 8 bit mode */ - emit2("ld a,%s", s); - emit2("out (%s),a", aop->aopu.aop_dir); - } + if (strcmp(s, "a")) + emit2("ld a, %s", s); + emit2("out (%s), a", aop->aopu.aop_dir); } break; case AOP_REG: - if (!strcmp(s, "!*hl")) + if (!strcmp(aop->aopu.aop_reg[offset]->name, s)) + ; + else if (!strcmp(s, "!*hl")) emit2("ld %s,!*hl", aop->aopu.aop_reg[offset]->name); else - emit2("ld %s,%s", aop->aopu.aop_reg[offset]->name, s); + emit2("ld %s, %s", aop->aopu.aop_reg[offset]->name, s); spillPairReg(aop->aopu.aop_reg[offset]->name); break; case AOP_IY: - wassert(!IS_GB); if (!canAssignToPtr(s)) { - emit2("ld a,%s", s); + emit2("ld a, %s", s); setupPair(PAIR_IY, aop, offset); - emit2("ld !*iyx,a", offset); + emit2("ld !*iyx, a", offset); } else { setupPair(PAIR_IY, aop, offset); - emit2("ld !*iyx,%s", offset, s); + emit2("ld !*iyx, %s", offset, s); } break; @@ -2302,7 +2375,7 @@ static void aopPut(asmop *aop, const char *s, int offset) { // wassert (IS_GB); /* PENDING: for re-target */ if (!strcmp(s, "!*hl") || !strcmp(s, "(hl)") || !strcmp(s, "[hl]")) { - emit2("ld a,!*hl"); + emit2("ld a, !*hl"); s = "a"; } else if (strstr(s, "(ix)") || strstr(s, "(iy)")) { emit2("ld a, %s", s); @@ -2310,42 +2383,43 @@ static void aopPut(asmop *aop, const char *s, int offset) { } setupPair(PAIR_HL, aop, offset); - emit2("ld !*hl,%s", s); + emit2("ld !*hl, %s", s); break; case AOP_EXSTK: - wassert(!IS_GB); - if (!canAssignToPtr(s)) { - emit2("ld a,%s", s); - setupPair(PAIR_IY, aop, offset); - emit2("ld !*iyx,a", offset); - } else { - setupPair(PAIR_IY, aop, offset); - emit2("ld !*iyx,%s", offset, s); + if (!IY_RESERVED) { + if (!canAssignToPtr(s)) { + emit2("ld a, %s", s); + setupPair(PAIR_IY, aop, offset); + emit2("ld !*iyx, a", offset); + } else { + setupPair(PAIR_IY, aop, offset); + emit2("ld !*iyx, %s", offset, s); + } + break; } - break; case AOP_STK: - if (IS_GB) { + if (aop->type == AOP_EXSTK) { /* PENDING: re-target */ if (!strcmp(s, "!*hl") || !strcmp(s, "(hl)") || !strcmp(s, "[hl]")) { - emit2("ld a,!*hl"); + emit2("ld a, !*hl"); s = "a"; } - setupPair(PAIR_HL, aop, offset); + pointPairToAop(PAIR_HL, aop, offset); if (!canAssignToPtr(s)) { - emit2("ld a,%s", s); - emit2("ld !*hl,a"); + emit2("ld a, %s", s); + emit2("ld !*hl, a"); } else - emit2("ld !*hl,%s", s); + emit2("ld !*hl, %s", s); } else { if (aop->aopu.aop_stk >= 0) offset += _G.stack.param_offset; if (!canAssignToPtr(s)) { - emit2("ld a,%s", s); - emit2("ld !*ixx,a", aop->aopu.aop_stk + offset); + emit2("ld a, %s", s); + emit2("ld !*ixx, a", aop->aopu.aop_stk + offset); } else { - emit2("ld !*ixx,%s", aop->aopu.aop_stk + offset, s); + emit2("ld !*ixx, %s", aop->aopu.aop_stk + offset, s); } } break; @@ -2353,7 +2427,7 @@ static void aopPut(asmop *aop, const char *s, int offset) { case AOP_CRY: /* if bit variable */ if (!aop->aopu.aop_dir) { - emit2("ld a,!zero"); + emit2("ld a, !zero"); emit2("rla"); } else { /* In bit space but not in C - cant happen */ @@ -2364,45 +2438,47 @@ static void aopPut(asmop *aop, const char *s, int offset) { case AOP_STR: aop->coff = offset; if (strcmp(aop->aopu.aop_str[offset], s)) { - emit2("ld %s,%s", aop->aopu.aop_str[offset], s); + emit2("ld %s, %s", aop->aopu.aop_str[offset], s); } spillPairReg(aop->aopu.aop_str[offset]); break; - case AOP_ACC: - aop->coff = offset; - if (!offset && (strcmp(s, "acc") == 0)) - break; - if (offset > 0) { - wassertl(0, "Tried to access past the end of A"); - } else { - wassert(aop->aopu.aop_str[offset]); - wassert(s); - if (strcmp(aop->aopu.aop_str[offset], s)) { - emit2("ld %s,%s", aop->aopu.aop_str[offset], s); - spillPairReg(aop->aopu.aop_str[offset]); - } - } - break; - - case AOP_HLREG: - wassert(offset < 2); - emit2("ld %s,%s", aop->aopu.aop_str[offset], s); - spillPairReg(aop->aopu.aop_str[offset]); - break; +#if 0 + case AOP_ACC: + aop->coff = offset; + if (!offset && (strcmp (s, "acc") == 0)) + break; + if (offset > 0) + { + wassertl (0, "Tried to access past the end of A"); + } + else + { + wassert (aop->aopu.aop_str[offset]); + wassert (s); + if (strcmp (aop->aopu.aop_str[offset], s)) + { + emit2 ("ld %s, %s", aop->aopu.aop_str[offset], s); + spillPairReg (aop->aopu.aop_str[offset]); + } + } + break; +#endif case AOP_PAIRPTR: setupPair(aop->aopu.aop_pairId, aop, offset); if (aop->aopu.aop_pairId == PAIR_IX) - emit2("ld !*ixx,%s", 0, s); + emit2("ld !*ixx, %s", 0, s); else if (aop->aopu.aop_pairId == PAIR_IY) - emit2("ld !*iyx,%s", 0, s); + emit2("ld !*iyx, %s", 0, s); else - emit2("ld (%s),%s", _pairs[aop->aopu.aop_pairId].name, s); + emit2("ld (%s), %s", _pairs[aop->aopu.aop_pairId].name, s); break; default: dbuf_destroy(&dbuf); + fprintf(stderr, "AOP_DIR: %d\n", AOP_DIR); + fprintf(stderr, "aop->type: %d\n", aop->type); werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); exit(0); @@ -2410,82 +2486,130 @@ static void aopPut(asmop *aop, const char *s, int offset) { dbuf_destroy(&dbuf); } -static void aopPut3(asmop *op1, int offset1, asmop *op2, int offset2) { - unsigned char cost = regalloc_dry_run_cost; - int fp_offset = 0; - int sp_offset = 0; +// Move, but try not to. Cannot use xor to zero, since xor resets the carry +// flag. +static void cheapMove(asmop *to, int to_offset, asmop *from, int from_offset, + bool a_dead) { + if (aopInReg(to, to_offset, A_IDX)) + a_dead = true; - if (!regalloc_dry_run) { - if ((op1->type == AOP_STK || op1->type == AOP_EXSTK)) { - fp_offset = op1->aopu.aop_stk + _G.stack.offset + offset1 + - (op1->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - sp_offset = fp_offset + _G.stack.pushed; - } - - if ((op1->type == AOP_STK || op1->type == AOP_EXSTK) && !sp_offset && - _G.omitFramePtr && op1->size == 1 && - (op2->type == AOP_REG && (op2->aopu.aop_reg[offset2]->rIdx == B_IDX || - op2->aopu.aop_reg[offset2]->rIdx == D_IDX || - op2->aopu.aop_reg[offset2]->rIdx == H_IDX) || - op2->type == AOP_ACC && op2->size == 1) && - !offset2) { + if (to->type == AOP_REG && from->type == AOP_REG) { + if (to->aopu.aop_reg[to_offset] == from->aopu.aop_reg[from_offset]) + return; + bool from_index = aopInReg(from, from_offset, IYL_IDX) || + aopInReg(from, from_offset, IYH_IDX); + bool to_index = + aopInReg(to, to_offset, IYL_IDX) || aopInReg(to, to_offset, IYH_IDX); + bool index = to_index || from_index; + if (!index || IS_EZ80_Z80) { + bool a = + aopInReg(to, to_offset, A_IDX) || aopInReg(from, from_offset, A_IDX); + if (!regalloc_dry_run) + aopPut(to, aopGet(from, from_offset, false), to_offset); + regalloc_dry_run_cost += 1 + (!a) + index; + return; + } + if (aopInReg(from, from_offset, IYL_IDX) && !to_index && a_dead) { + _push(PAIR_IY); + _pop(PAIR_AF); + cheapMove(to, to_offset, ASMOP_A, 0, true); + return; + } + if (from_index && !to_index && + _G.stack.pushed + _G.stack.offset + 2 <= 127 && !_G.omitFramePtr) { + _push(PAIR_IY); + if (!regalloc_dry_run) + emit2("ld %s, %d (ix)", aopGet(to, to_offset, false), + _G.stack.pushed + _G.stack.offset); + regalloc_dry_run_cost += 3; + _pop(PAIR_IY); + return; + } + + // Can't do it (todo: implement something there - will be expensive though, + // probably at least 7B of code). + regalloc_dry_run_cost += 100; + wassert(regalloc_dry_run); + } + + // Try to push to avoid setting up temporary stack pointer in hl or iy. + if ((to->type == AOP_STK || to->type == AOP_EXSTK) && _G.omitFramePtr && + (aopInReg(to, to_offset, A_IDX) || aopInReg(to, to_offset, B_IDX) || + aopInReg(to, to_offset, D_IDX) || aopInReg(to, to_offset, H_IDX) || + aopInReg(to, to_offset, IYH_IDX))) { + int fp_offset = to->aopu.aop_stk + to_offset + + (to->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (!sp_offset) { emit2("inc sp"); emit2("push %s", - op2->type == AOP_ACC + aopInReg(from, from_offset, A_IDX) ? "af" - : (op2->aopu.aop_reg[offset2]->rIdx == B_IDX + : (aopInReg(from, from_offset, B_IDX) ? "bc" - : (op2->aopu.aop_reg[offset2]->rIdx == D_IDX ? "de" - : "hl"))); + : (aopInReg(from, from_offset, D_IDX) + ? "de" + : (aopInReg(from, from_offset, H_IDX) ? "hl" + : "iy")))); emit2("inc sp"); - regalloc_dry_run_cost += 4; - } else - aopPut(op1, aopGet(op2, offset2, FALSE), offset1); + regalloc_dry_run_cost += 3 + aopInReg(from, from_offset, IYH_IDX); + return; + } } - regalloc_dry_run_cost = - cost + ld_cost(op1, offset2 < op2->size ? op2 : ASMOP_ZERO); -} + if (aopInReg(from, from_offset, A_IDX) && to->type == AOP_IY) { + emit2("ld (%s+%d), a", to->aopu.aop_dir, to_offset); + regalloc_dry_run_cost += 3; + } else if (!aopInReg(to, to_offset, A_IDX) && + !aopInReg(from, from_offset, A_IDX) && // Go through a. + (from->type == AOP_DIR || from->type == AOP_SFR || + to->type == AOP_STK && from->type == AOP_STK || + to->type == AOP_IY && + (from->type == AOP_EXSTK ) || + (to->type == AOP_HL|| + to->type == AOP_EXSTK) && + (aopInReg(from, from_offset, L_IDX) || + aopInReg(from, from_offset, H_IDX)))) { + if (!a_dead) + _push(PAIR_AF); -// Move, but try not to. -static void cheapMove(asmop *to, int to_offset, asmop *from, int from_offset) { - /* Todo: Longer list of moves that can be optimized out. */ - if (to->type == AOP_ACC && from->type == AOP_ACC && to_offset == from_offset) - return; - if (to->type == AOP_REG && from->type == AOP_REG && - to->aopu.aop_reg[to_offset] == from->aopu.aop_reg[from_offset]) - return; - if (to->type == AOP_HLREG && from->type == AOP_HLREG && - !strcmp(to->aopu.aop_str[to_offset], from->aopu.aop_str[from_offset])) - return; - aopPut3(to, to_offset, from, from_offset); + cheapMove(ASMOP_A, 0, from, from_offset, true); + cheapMove(to, to_offset, ASMOP_A, 0, true); + + if (!a_dead) + _pop(PAIR_AF); + } else { + if (!regalloc_dry_run) + aopPut(to, aopGet(from, from_offset, false), to_offset); + + regalloc_dry_run_cost += + ld_cost(to, from_offset < from->size ? from : ASMOP_ZERO); + } } -static void commitPair(asmop *aop, PAIR_ID id, const iCode *ic, - bool dont_destroy) { - int fp_offset = aop->aopu.aop_stk + _G.stack.offset + - (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - int sp_offset = fp_offset + _G.stack.pushed; +static void +commitPair(asmop *aop, PAIR_ID id, const iCode *ic, + bool dont_destroy) // Obsolete. Replace uses by genMove or genMove_o. +{ + int fp_offset = + aop->aopu.aop_stk + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (getPairId(aop) == id) + return; /* Stack positions will change, so do not assume this is possible in the cost * function. */ - if (!regalloc_dry_run && !IS_GB && + if (!regalloc_dry_run && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && !sp_offset && - ((!IS_RAB && id == PAIR_HL) || id == PAIR_IY) && !dont_destroy) { + ((id == PAIR_HL) || id == PAIR_IY) && !dont_destroy) { emit2("ex (sp), %s", _pairs[id].name); - regalloc_dry_run_cost += ((id == PAIR_IY || IS_RAB) ? 2 : 1); + regalloc_dry_run_cost += ((id == PAIR_IY) ? 2 : 1); spillPair(id); - } else if ((IS_RAB || IS_TLCS90) && - (aop->type == AOP_STK || aop->type == AOP_EXSTK) && - (id == PAIR_HL || id == PAIR_IY) && - (id == PAIR_HL && abs(fp_offset) <= 127 && aop->type == AOP_STK || - abs(sp_offset) <= 127)) { - if (abs(sp_offset) <= 127) - emit2("ld (sp + %d), %s", sp_offset, - id == PAIR_IY ? "iy" : "hl"); /* Relative to stack pointer. */ - else - emit2("ld (ix + %d), hl", fp_offset); /* Relative to frame pointer. */ - regalloc_dry_run_cost += (id == PAIR_HL ? 2 : 3); + } else if (IS_EZ80_Z80 && aop->type == AOP_STK) { + emit2("ld %d (ix), %s", fp_offset, _pairs[id].name); + regalloc_dry_run_cost += 3; } else if (!regalloc_dry_run && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && !sp_offset) { emit2("inc sp"); @@ -2495,12 +2619,13 @@ static void commitPair(asmop *aop, PAIR_ID id, const iCode *ic, } /* PENDING: Verify this. */ - else if (id == PAIR_HL && requiresHL(aop) && (IS_GB || IY_RESERVED)) { + else if (id == PAIR_HL && requiresHL(aop) && + (IY_RESERVED && aop->type != AOP_HL && aop->type != AOP_IY)) { if (bitVectBitValue(ic->rSurv, D_IDX)) _push(PAIR_DE); if (!regalloc_dry_run) { - emit2("ld a,l"); - emit2("ld d,h"); + emit2("ld a, l"); + emit2("ld d, h"); aopPut(aop, "a", 0); aopPut(aop, "d", 1); } @@ -2510,45 +2635,700 @@ static void commitPair(asmop *aop, PAIR_ID id, const iCode *ic, _pop(PAIR_DE); } else { /* Special cases */ - if (aop->type == AOP_IY && aop->size == 2) { + if ((aop->type == AOP_IY || aop->type == AOP_HL) && + aop->size == 2) { if (!regalloc_dry_run) { - emit2("ld (%s),%s", aopGetLitWordLong(aop, 0, FALSE), _pairs[id].name); + emit2("ld (%s), %s", aopGetLitWordLong(aop, 0, FALSE), _pairs[id].name); } regalloc_dry_run_cost += (id == PAIR_HL ? 3 : 4); } else { switch (id) { case PAIR_BC: - cheapMove(aop, 0, ASMOP_C, 0); - cheapMove(aop, 1, ASMOP_B, 0); + cheapMove(aop, 0, ASMOP_C, 0, true); + cheapMove(aop, 1, ASMOP_B, 0, true); break; case PAIR_DE: - cheapMove(aop, 0, ASMOP_E, 0); - cheapMove(aop, 1, ASMOP_D, 0); + if (aop->type == AOP_REG && + aop->aopu.aop_reg[0]->rIdx == L_IDX && + aop->aopu.aop_reg[1]->rIdx == H_IDX && !dont_destroy) { + emit2("ex de, hl"); + regalloc_dry_run_cost++; + } else { + cheapMove(aop, 0, ASMOP_E, 0, true); + cheapMove(aop, 1, ASMOP_D, 0, true); + } break; case PAIR_HL: if (aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == H_IDX && aop->aopu.aop_reg[1]->rIdx == L_IDX) { - cheapMove(ASMOP_A, 0, ASMOP_L, 0); - cheapMove(aop, 1, ASMOP_H, 0); - cheapMove(aop, 0, ASMOP_A, 0); + cheapMove(ASMOP_A, 0, ASMOP_L, 0, true); + cheapMove(aop, 1, ASMOP_H, 0, true); + cheapMove(aop, 0, ASMOP_A, 0, true); } else if (aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == H_IDX) // Do not overwrite upper byte. { - cheapMove(aop, 1, ASMOP_H, 0); - cheapMove(aop, 0, ASMOP_L, 0); + cheapMove(aop, 1, ASMOP_H, 0, true); + cheapMove(aop, 0, ASMOP_L, 0, true); + } else if (aop->type == AOP_REG && + aop->aopu.aop_reg[0]->rIdx == E_IDX && + aop->aopu.aop_reg[1]->rIdx == D_IDX && !dont_destroy) { + emit2("ex de, hl"); + regalloc_dry_run_cost++; } else { - cheapMove(aop, 0, ASMOP_L, 0); - cheapMove(aop, 1, ASMOP_H, 0); + cheapMove(aop, 0, ASMOP_L, 0, true); + cheapMove(aop, 1, ASMOP_H, 0, true); } break; + case PAIR_IY: + cheapMove(aop, 0, ASMOP_IYL, 0, true); + cheapMove(aop, 1, ASMOP_IYH, 0, true); + break; default: wassertl(0, "Unknown pair id in commitPair()"); + fprintf(stderr, "pair %s\n", _pairs[id].name); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genCopyStack - Copy the value - stack to stack only */ +/*-----------------------------------------------------------------*/ +static void genCopyStack(asmop *result, int roffset, asmop *source, int soffset, + int n, bool *assigned, int *size, bool a_free, + bool hl_free, bool really_do_it_now) { + for (int i = 0; i < n;) { + if (assigned[i]) { + i++; + continue; + } + + if (!aopOnStack(result, roffset + i, 1) || + !aopOnStack(source, soffset + i, 1)) { + i++; + continue; + } + + int source_fp_offset = + source->aopu.aop_stk + soffset + + (source->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int result_fp_offset = + result->aopu.aop_stk + roffset + + (result->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + + if (result_fp_offset == source_fp_offset && + !regalloc_dry_run) // Stack locations can change, so in dry run do not + // assume stack coalescing will happen. + { + assigned[i] = true; + i++; + continue; + } + if (i + 1 < n && !assigned[i + 1] && hl_free && + (IS_RAB || IS_EZ80_Z80 || IS_TLCS90)) { + if (!regalloc_dry_run) { + emit2("ld hl, %s", aopGet(source, soffset + i, false)); + emit2("ld %s, hl", aopGet(result, roffset + i, false)); + } + cost2(6 - 2 * IS_RAB, 0, 0, 22, 0, 21, 10); + + spillPair(PAIR_HL); + + assigned[i] = true; + assigned[i + 1] = true; + (*size) -= 2; + i += 2; + continue; + } + + if (a_free || really_do_it_now) { + cheapMove(result, roffset + i, source, soffset + i, a_free); + assigned[i] = true; + (*size)--; + i++; + continue; + } + + i++; + } + + wassertl_bt(*size >= 0, + "genCopyStack() copied more than there is to be copied."); +} + +/*-----------------------------------------------------------------*/ +/* genCopy - Copy the value from one reg/stk asmop to another */ +/*-----------------------------------------------------------------*/ +static void genCopy(asmop *result, int roffset, asmop *source, int soffset, + int sizex, bool a_dead, bool hl_dead, bool de_dead) { + int regsize, size, + n = (sizex < source->size - soffset) ? sizex : (source->size - soffset); + bool assigned[8] = {false, false, false, false, false, false, false, false}; + bool a_free, hl_free; + int cached_byte = -1; + bool pushed_a = false; + + wassertl_bt(n <= 8, "Invalid size for genCopy()."); + wassertl_bt(aopRS(source), "Invalid source type."); + wassertl_bt(aopRS(result), "Invalid result type."); + + a_dead |= (result->regs[A_IDX] >= 0); + hl_dead |= (result->regs[L_IDX] >= 0 && result->regs[H_IDX] >= 0); + de_dead |= (result->regs[E_IDX] >= 0 && result->regs[D_IDX] >= 0); + + size = n; + regsize = 0; + for (int i = 0; i < n; i++) + regsize += (source->type == AOP_REG); + + // Do nothing for coalesced bytes. + for (int i = 0; i < n; i++) + if (result->type == AOP_REG && source->type == AOP_REG && + result->aopu.aop_reg[roffset + i] == + source->aopu.aop_reg[soffset + i]) { + assigned[i] = true; + regsize--; + size--; + } + + // Move everything from registers to the stack. + for (int i = 0; i < n;) { + bool a_free = a_dead && (source->regs[A_IDX] < 0 || + assigned[source->regs[A_IDX] - soffset] || + i == source->regs[A_IDX] - soffset); + bool hl_free = + hl_dead && + (source->regs[L_IDX] < 0 || assigned[source->regs[L_IDX] - soffset] || + i == source->regs[L_IDX] - soffset) && + (source->regs[H_IDX] < 0 || assigned[source->regs[H_IDX] - soffset] || + i == source->regs[H_IDX] - soffset); + bool de_free = + de_dead && + (source->regs[E_IDX] < 0 || assigned[source->regs[E_IDX] - soffset] || + i == source->regs[E_IDX] - soffset) && + (source->regs[D_IDX] < 0 || assigned[source->regs[D_IDX] - soffset] || + i == source->regs[D_IDX] - soffset); + + int fp_offset = result->aopu.aop_stk + + (result->aopu.aop_stk > 0 ? _G.stack.param_offset : 0) + + roffset + i; + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (!IS_GB && !IS_RAB && + !(IS_TLCS90 && + optimize + .codeSpeed) && // The gbz80 doesn't have ex (sp), hl. The Rabbits + // and tlcs90 have it, but ld 0 (sp), hl is faster. + // For the Rabbits, they are also the same size. + i + 1 < n && + aopOnStack(result, roffset + i, 2) && + aopInReg(source, soffset + i, HL_IDX) && hl_dead && !sp_offset && + !regalloc_dry_run) // Stack positions will change, so do not assume this + // is possible in the cost function. + { + emit2("ex (sp), hl"); + cost2(1 + IS_RAB, 19, 16, 15, 0, 14, 5); + spillPair(PAIR_HL); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (i + 1 < n && aopOnStack(result, roffset + i, 2) && + (abs(fp_offset) <= 127 && !_G.omitFramePtr || + IS_RAB && sp_offset <= 255 || IS_TLCS90 && sp_offset <= 127) && + (aopInReg(source, soffset + i, HL_IDX) && IS_RAB || + (getPairId_o(source, soffset + i) != PAIR_INVALID && + (IS_EZ80_Z80 || IS_TLCS90)))) { + bool use_sp = IS_RAB && sp_offset <= 255 || IS_TLCS90 && sp_offset <= 127; + if (!regalloc_dry_run) + emit2("ld %d %s, %s", use_sp ? sp_offset : fp_offset, + use_sp ? "(sp)" : "(ix)", + _pairs[getPairId_o(source, soffset + i)].name); + cost2(3 - IS_RAB, 0, 0, 11, 0, 12, 5); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (IS_RAB && i + 1 < n && aopOnStack(result, roffset + i, 2) && + aopInReg(source, soffset + i, IY_IDX) && sp_offset <= 255) { + emit2("ld %d (sp), iy", sp_offset); + cost2(3, 0, 0, 13, 0, 0, 0); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (i + 1 < n && aopOnStack(result, roffset + i, 2) && + getPairId_o(source, soffset + i) != PAIR_INVALID && !sp_offset && + !regalloc_dry_run) // Stack positions will change, so do not + // assume this is possible in the cost + // function. + { + bool iy = aopInReg(source, soffset + i, IY_IDX); + emit2("inc sp"); + emit2("inc sp"); + emit2("push %s", _pairs[getPairId_o(source, soffset + i)].name); + cost2(3 + iy, 23 + 4 * iy, 19 + 3 * iy, 14 + 2 * iy, 32, 16, 5 + iy); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (IS_RAB && i + 1 < n && aopOnStack(result, roffset + i, 2) && + aopInReg(source, soffset + i, DE_IDX) && + (sp_offset <= 255 || + abs(fp_offset) <= 127 && !_G.omitFramePtr)) { + bool use_sp = (sp_offset <= 255); + emit2("ex de, hl"); + if (!regalloc_dry_run) + emit2("ld %d %s, hl", use_sp ? sp_offset : fp_offset, + use_sp ? "(sp)" : "(ix)"); + emit2("ex de, hl"); + cost2(4, 0, 0, 15, 0, 0, 0); + spillPair(PAIR_HL); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (!IS_GB && i + 1 < n && aopOnStack(result, roffset + i, 2) && + requiresHL(result) && aopInReg(source, soffset + i, HL_IDX)) { + if (!de_free) + _push(PAIR_DE); + emit2("ex de, hl"); + cost2(1, 4, 3, 2, 0, 2, 1); + spillPair(PAIR_HL); + genCopy(result, roffset + i, ASMOP_DE, 0, 2, a_free, true, true); + if (!de_free) + _pop(PAIR_DE); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } else if (aopOnStack(result, roffset + i, 1) && requiresHL(result) && + !hl_free) { + _push(PAIR_HL); + cheapMove(result, roffset + i, source, soffset + i, true); + _pop(PAIR_HL); + assigned[i] = true; + regsize--; + size--; + i++; + } else if (aopRS(source) && !aopOnStack(source, soffset + i, 1) && + aopOnStack(result, roffset + i, 1)) { + cheapMove(result, roffset + i, source, soffset + i, true); + assigned[i] = true; + regsize--; + size--; + i++; + } else // This byte is not a register-to-stack copy. + i++; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have. + a_free = a_dead; + hl_free = hl_dead; + for (int i = 0; i < n; i++) { + asmop *operand; + int offset; + + if (!assigned[i]) { + operand = source; + offset = soffset + i; + } else { + operand = result; + offset = roffset + i; + } + + if (aopInReg(operand, offset, A_IDX)) + a_free = false; + else if (aopInReg(operand, offset, L_IDX) || + aopInReg(operand, offset, H_IDX)) + hl_free = FALSE; + } + genCopyStack(result, roffset, source, soffset, n, assigned, &size, a_free, + hl_free, false); + + // Now do the register shuffling. + + // Try to use: + // Rabbits: ld hl, iy; ld iy, hl + // TLCS-90 ld rr, rr + // eZ80 lea rr, iy. + // All: push rr / pop iy + // All: push iy / pop rr + for (int i = 0; i + 1 < n; i++) { + if (assigned[i] || assigned[i + 1]) + continue; + + for (int j = 0; j + 1 < n; j++) { + if (!assigned[j] && i != j && i + 1 != j && + !aopOnStack(result, roffset + i, 2) && + !aopOnStack(source, soffset + i, 1) && + (result->aopu.aop_reg[roffset + i] == + source->aopu.aop_reg[soffset + j] || + result->aopu.aop_reg[roffset + i + 1] == + source->aopu.aop_reg[soffset + j])) + goto skip_byte_push_iy; // We can't write this one without overwriting + // the source. + } + + if (IS_RAB && + (getPairId_o(result, roffset + i) == PAIR_HL || + getPairId_o(result, roffset + i) == PAIR_IY) && + (getPairId_o(source, soffset + i) == PAIR_HL || + getPairId_o(source, soffset + i) == PAIR_IY)) { + emit2("ld %s, %s", _pairs[getPairId_o(result, roffset + i)].name, + _pairs[getPairId_o(source, soffset + i)].name); + cost(2, 4); + } else if (IS_TLCS90 && getPairId_o(result, roffset + i) != PAIR_INVALID && + getPairId_o(source, soffset + i) != PAIR_INVALID) { + emit2("ld %s, %s", _pairs[getPairId_o(result, roffset + i)].name, + _pairs[getPairId_o(source, soffset + i)].name); + regalloc_dry_run_cost += 1 + (!aopInReg(result, roffset + i, HL_IDX) && + !aopInReg(source, soffset + i, HL_IDX)); + } else if (IS_EZ80_Z80 && + getPairId_o(result, roffset + i) != PAIR_INVALID && + aopInReg(source, soffset + i, IY_IDX)) { + emit2("lea %s, iy, #0", _pairs[getPairId_o(result, roffset + i)].name); + cost(3, 3); + } else if (aopInReg(result, roffset + i, IY_IDX) && + getPairId_o(source, soffset + i) != PAIR_INVALID || + getPairId_o(result, roffset + i) != PAIR_INVALID && + aopInReg(source, soffset + i, IY_IDX)) { + emit2("push %s", _pairs[getPairId_o(source, soffset + i)].name); + emit2("pop %s", _pairs[getPairId_o(result, roffset + i)].name); + regalloc_dry_run_cost += 3; + } else + continue; + + regsize -= 2; + size -= 2; + assigned[i] = true; + assigned[i + 1] = true; + + skip_byte_push_iy:; + } + + if (!IS_GB) { + int ex[4] = {-2, -2, -2, -2}; // Swapped bytes + bool no = false; // Still needed byte would be overwritten + + // Find L and check that it is exchanged with E, find H and check that it is + // exchanged with D. + for (int i = 0; i < n; i++) { + if (assigned[i] && (aopInReg(result, roffset + i, E_IDX) || + aopInReg(result, roffset + i, L_IDX) || + aopInReg(result, roffset + i, D_IDX) || + aopInReg(result, roffset + i, H_IDX))) + no = true; + + if (!assigned[i] && aopInReg(source, soffset + i, E_IDX)) + if (aopInReg(result, roffset + i, L_IDX)) + ex[0] = i; + else + no = true; + if (!assigned[i] && aopInReg(source, soffset + i, L_IDX)) + if (aopInReg(result, roffset + i, E_IDX)) + ex[1] = i; + else + no = true; + if (!assigned[i] && aopInReg(source, soffset + i, D_IDX)) + if (aopInReg(result, roffset + i, H_IDX)) + ex[2] = i; + else + no = true; + if (!assigned[i] && aopInReg(source, soffset + i, H_IDX)) + if (aopInReg(result, roffset + i, D_IDX)) + ex[3] = i; + else + no = true; + } + + int exsum = (ex[0] >= 0) + (ex[1] >= 0) + (ex[2] >= 0) + (ex[3] >= 0); + + if (!no && exsum >= 2 && hl_dead && de_dead) { + emit2("ex de, hl"); + cost2(1, 4, 3, 2, 0, 2, 1); + if (ex[0] >= 0) + assigned[ex[0]] = TRUE; + if (ex[1] >= 0) + assigned[ex[1]] = TRUE; + if (ex[2] >= 0) + assigned[ex[2]] = TRUE; + if (ex[3] >= 0) + assigned[ex[3]] = TRUE; + regsize -= exsum; + size -= exsum; + } + } + + while (regsize && result->type == AOP_REG && source->type == AOP_REG) { + int i; + + // Find lowest byte that can be assigned and needs to be assigned. + for (i = 0; i < n; i++) { + if (assigned[i]) + continue; + + for (int j = 0; j < n; j++) { + if (!assigned[j] && i != j && + result->aopu.aop_reg[roffset + i] == + source->aopu.aop_reg[soffset + j]) + goto skip_byte; // We can't write this one without overwriting the + // source. + } + + break; // Found byte that can be written safely. + + skip_byte:; + } + + if (i < n) { + cheapMove(result, roffset + i, source, soffset + i, + false); // We can safely assign a byte. + regsize--; + size--; + assigned[i] = true; + continue; + } + + // No byte can be assigned safely (i.e. the assignment is a permutation). + // Cache one in the accumulator. + + if (cached_byte != -1) { + // Already one cached. Can happen when the assignment is a permutation + // consisting of multiple cycles. + cheapMove(result, roffset + cached_byte, ASMOP_A, 0, true); + cached_byte = -1; + continue; + } + + for (i = 0; i < n; i++) + if (!assigned[i]) + break; + + wassertl_bt( + i != n, + "genCopy error: Trying to cache non-existant byte in accumulator."); + if (!a_free && !pushed_a) { + _push(PAIR_AF); + pushed_a = TRUE; + } + cheapMove(ASMOP_A, 0, source, soffset + i, true); + regsize--; + size--; + assigned[i] = TRUE; + cached_byte = i; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have now. + a_free = a_dead; + hl_free = hl_dead; + for (int i = 0; i < n; i++) { + if (!assigned[i]) + continue; + if (aopInReg(result, roffset + i, A_IDX)) + a_free = false; + else if (aopInReg(result, roffset + i, L_IDX) || + aopInReg(result, roffset + i, H_IDX)) + hl_free = false; + } + genCopyStack(result, roffset, source, soffset, n, assigned, &size, a_free, + hl_free, false); + + // Last, move everything from stack to registers. + for (int i = 0; i < n;) { + if (i + 1 < n && source->type == AOP_STK && + (aopInReg(result, roffset + i, HL_IDX) && IS_RAB || + (aopInReg(result, roffset + i, BC_IDX) || + aopInReg(result, roffset + i, DE_IDX) || + aopInReg(result, roffset + i, HL_IDX) || + aopInReg(result, roffset + i, IY_IDX)) && + (IS_EZ80_Z80 || IS_TLCS90))) { + if (!regalloc_dry_run) + emit2("ld %s, %s", _pairs[getPairId_o(result, roffset + i)].name, + aopGet(source, soffset + i, false)); + cost2(3 - IS_RAB, 0, 0, 11, 0, 9, 5); + assigned[i] = true; + assigned[i + 1] = true; + size -= 2; + i += 2; + } else if (i + 1 < n && source->type == AOP_STK && + aopInReg(result, roffset + i, DE_IDX) && IS_RAB) { + bool hl_free = + hl_dead && + (result->regs[L_IDX] < 0 || + !assigned[result->regs[L_IDX] - roffset]) && + (result->regs[H_IDX] < 0 || !assigned[result->regs[H_IDX] - roffset]); + if (!hl_free) + emit2("ex de, hl"); + if (!regalloc_dry_run) + emit2("ld hl, %s", aopGet(source, soffset + i, false)); + emit2("ex de, hl"); + cost2(3 + !hl_free, 0, 0, 13 + !hl_free * 2, 0, 0, 0); + spillPair(PAIR_HL); + assigned[i] = true; + assigned[i + 1] = true; + size -= 2; + i += 2; + } else if (aopRS(result) && aopOnStack(source, soffset + i, 1) && + !aopOnStack(result, roffset + i, 1)) { + cheapMove(result, roffset + i, source, soffset + i, true); + assigned[i] = true; + size--; + i++; + } else // This byte is not a register-to-stack copy. + i++; + } + + // Free a reg to copy (stack-to-stack) whatever is left. + if (size) { + a_free = a_dead && (result->regs[A_IDX] < 0 || + result->regs[A_IDX] >= roffset + source->size); + hl_free = hl_dead && + (result->regs[L_IDX] < 0 || + result->regs[L_IDX] >= roffset + source->size) && + (result->regs[H_IDX] < 0 || + result->regs[H_IDX] >= roffset + source->size); + if (!a_free) + _push(PAIR_AF); + genCopyStack(result, roffset, source, soffset, n, assigned, &size, true, + hl_free, true); + if (!a_free) + _pop(PAIR_AF); + } + + wassertl_bt(size >= 0, "genCopy() copied more than there is to be copied."); + + a_free = a_dead && (result->regs[A_IDX] < 0 || + result->regs[A_IDX] >= roffset + source->size); + + // Place leading zeroes. + + // todo + + if (cached_byte != -1) + cheapMove(result, roffset + cached_byte, ASMOP_A, 0, true); + + if (pushed_a) + _pop(PAIR_AF); +} + +/*-----------------------------------------------------------------*/ +/* genMove_o - Copy part of one asmop to another */ +/*-----------------------------------------------------------------*/ +static void genMove_o(asmop *result, int roffset, asmop *source, int soffset, + int size, bool a_dead_global, bool hl_dead_global, + bool de_dead_global) { + emitDebug("; genMove_o"); + + if ((result->type == AOP_REG || result->type == AOP_STK || + result->type == AOP_EXSTK) && + (source->type == AOP_REG || + source->type == AOP_STK)) // Todo: enable for source->type == AOP_EXSTK + // once implemented in genCopy(). + { + int csize = size > source->size - soffset ? source->size - soffset : size; + genCopy(result, roffset, source, soffset, csize, a_dead_global, + hl_dead_global, de_dead_global); + roffset += csize; + size -= csize; + genMove_o(result, roffset, ASMOP_ZERO, 0, size, + a_dead_global && result->regs[A_IDX] < roffset, + hl_dead_global && result->regs[H_IDX] < roffset && + result->regs[L_IDX] < roffset, + de_dead_global && result->regs[D_IDX] < roffset && + result->regs[E_IDX] < roffset); + return; + } + + bool zeroed_a = false; + long value_hl = -1; + bool a_dead = a_dead_global; + bool hl_dead = hl_dead_global; + for (unsigned int i = 0; i < size;) { + if ((IS_EZ80_Z80 || IS_RAB || IS_TLCS90) && i + 1 < size && + result->type == AOP_STK && source->type == AOP_LIT && + (value_hl >= 0 && aopIsLitVal(source, soffset + i, 2, value_hl) || + hl_dead)) { + if (value_hl < 0 || !aopIsLitVal(source, soffset + i, 2, value_hl)) + fetchLitPair(PAIR_HL, source, soffset + i); + if (!regalloc_dry_run) + emit2("ld %s, hl", aopGet(result, roffset + i, false)); + cost2(3 - IS_RAB, 0, 0, 11, 0, 12, 5); + regalloc_dry_run_cost += 3; + value_hl = + ullFromVal(source->aopu.aop_lit) >> ((soffset + i) * 8) & 0xffff; + i += 2; + continue; + } else if (getPairId_o(source, soffset + i) != PAIR_INVALID && + (result->type == AOP_IY || result->type == AOP_DIR)) { + emit2("ld !mems, %s", aopGetLitWordLong(result, roffset + i, false), + _pairs[getPairId_o(source, soffset + i)].name); + regalloc_dry_run_cost += + 3 + (getPairId_o(source, soffset + i) != PAIR_HL); + i += 2; + continue; + } + + else if (i + 1 < size && getPairId_o(result, roffset + i) != PAIR_INVALID) { + fetchPairLong(getPairId_o(result, roffset + i), source, 0, soffset + i); + i += 2; + continue; + } + + // Cache a copy of zero in a. + if (result->type != AOP_REG && + aopIsLitVal(source, soffset + i, 2, 0x0000) && !zeroed_a && a_dead) { + emit3(A_XOR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost += 1; + zeroed_a = true; + } + + if (result->type == AOP_HL && a_dead_global && + (!hl_dead_global || source->regs[L_IDX] != -1 || + source->regs[H_IDX] != -1)) { + if (!aopIsLitVal(source, soffset + i, 1, 0x00) || !zeroed_a) { + cheapMove(ASMOP_A, 0, source, soffset + i, true); + zeroed_a = aopIsLitVal(source, soffset + i, 1, 0x00); } + emit2("ld !mems, a", aopGetLitWordLong(result, roffset + i, FALSE)); + regalloc_dry_run_cost += 3; + } else if (aopIsLitVal(source, soffset + i, 1, 0x00) && zeroed_a) + cheapMove(result, roffset + i, ASMOP_A, 0, false); + else if (aopIsLitVal(source, soffset + i, 1, 0x00) && + aopInReg(result, roffset + i, A_IDX)) { + emit3(A_XOR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost += 1; + zeroed_a = true; + } else { + cheapMove(result, roffset + i, source, soffset + i, a_dead_global); + zeroed_a = false; } + + if (aopInReg(result, roffset + i, A_IDX)) + a_dead = false; + if (aopInReg(result, roffset + i, H_IDX) || + aopInReg(result, roffset + i, L_IDX)) + hl_dead = false; + + i++; } } +/*-----------------------------------------------------------------*/ +/* genMove - Copy the value from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void genMove(asmop *result, asmop *source, bool a_dead, bool hl_dead, + bool de_dead) { + genMove_o(result, 0, source, 0, result->size, a_dead, hl_dead, false); +} + /*-----------------------------------------------------------------*/ /* getDataSize - get the operand data size */ /*-----------------------------------------------------------------*/ @@ -2574,40 +3354,40 @@ static void adjustStack(int n, bool af_free, bool bc_free, bool hl_free, (optimize.codeSize ? 2 + (af_free || bc_free || hl_free || iy_free || n < 0) * 2 : 1)) { - emit2("add sp, #%d", n); + emit2("add sp, !immed%d", n); cost(3, 6); n -= n; } else if (abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : (optimize.codeSize ? 8 : 5)) && hl_free) { spillCached(); - emit2("ld hl,#%d", n); - emit2("add hl,sp"); - emit2("ld sp,hl"); - cost2(5, 27, 20, 10, 28, 18); + emit2("ld hl, !immed%d", n); + emit2("add hl, sp"); + emit2("ld sp, hl"); + cost2(5, 27, 20, 10, 28, 18, 4); regalloc_dry_run_cost += 5; n -= n; } else if (!IS_GB && abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : 8) && iy_free) { spillCached(); - emit2("ld iy,#%d", n); - emit2("add iy,sp"); - emit2("ld sp,iy"); + emit2("ld iy, !immed%d", n); + emit2("add iy, sp"); + emit2("ld sp, iy"); regalloc_dry_run_cost += 8; n -= n; } else if (abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : 8) && bc_free) { emit2("ld c, l"); emit2("ld b, h"); - emit2("ld hl,#%d", n); - emit2("add hl,sp"); - emit2("ld sp,hl"); + emit2("ld hl, !immed%d", n); + emit2("add hl, sp"); + emit2("ld sp, hl"); emit2("ld l, c"); emit2("ld h, b"); regalloc_dry_run_cost += 9; n -= n; } - while (abs(n) > 1) { + while (abs(n)) { if ((IS_RAB || IS_GB) && abs(n) > (optimize.codeSize ? 2 : 1)) { int d; if (n > 127) @@ -2616,52 +3396,43 @@ static void adjustStack(int n, bool af_free, bool bc_free, bool hl_free, d = -128; else d = n; - emit2("add sp, #%d", d); + emit2("add sp, !immed%d", d); cost(2, IS_GB ? 16 : 4); n -= d; - } else if (n >= 2 && af_free && (IS_Z80 || optimize.codeSize)) { + } else if (n >= 2 && af_free && + ((IS_Z80 || IS_Z80N) || optimize.codeSize)) { emit2("pop af"); - cost2(1, 10, 9, 7, 12, 10); + cost2(1, 10, 9, 7, 12, 10, 3); n -= 2; - } else if (n <= -2 && (IS_Z80 || optimize.codeSize)) { + } else if (n <= -2 && ((IS_Z80 || IS_Z80N) || optimize.codeSize)) { emit2("push af"); - cost2(1, 10, 11, 7, 12, 10); + cost2(1, 10, 11, 7, 12, 10, 3); n += 2; - } else if (n >= 2 && bc_free && (IS_Z80 || optimize.codeSize)) { + } else if (n >= 2 && bc_free && + ((IS_Z80 || IS_Z80N) || optimize.codeSize)) { emit2("pop bc"); - cost2(1, 10, 9, 7, 12, 10); + cost2(1, 10, 9, 7, 12, 10, 3); n -= 2; - } else if (n >= 2 && hl_free && (IS_Z80 || optimize.codeSize)) { + } else if (n >= 2 && hl_free && + ((IS_Z80 || IS_Z80N) || optimize.codeSize)) { emit2("pop hl"); - cost2(1, 10, 9, 7, 12, 10); + cost2(1, 10, 9, 7, 12, 10, 3); n -= 2; } else if (IS_TLCS90 && n >= 2 && iy_free && optimize.codeSize) { emit2("pop iy"); cost(1, 10); n -= 2; - } else if (n >= 2) { + } else if (n >= 1) { emit2("inc sp"); - emit2("inc sp"); - cost2(2, 12, 8, 4, 16, 8); - n -= 2; - } else if (n <= -2) { + cost2(1, 6, 4, 2, 8, 4, 1); + n--; + } else if (n <= -1) { emit2("dec sp"); - emit2("dec sp"); - cost2(2, 12, 8, 4, 16, 8); - n += 2; + cost2(1, 6, 4, 2, 8, 4, 1); + n++; } } - if (n == 1) { - emit2("inc sp"); - cost2(1, 6, 4, 2, 8, 4); - n--; - } else if (n == -1) { - emit2("dec sp"); - cost2(1, 6, 4, 2, 8, 4); - n++; - } - wassert(!n); } @@ -2672,41 +3443,12 @@ static void movLeft2Result(operand *left, int offl, operand *result, int offr, int sign) { if (!sameRegs(AOP(left), AOP(result)) || (offl != offr)) { if (!sign) - cheapMove(AOP(result), offr, AOP(left), offl); + cheapMove(AOP(result), offr, AOP(left), offl, true); else { if (getDataSize(left) == offl + 1) { - cheapMove(ASMOP_A, 0, AOP(left), offl); - cheapMove(AOP(result), offr, ASMOP_A, 0); - } - } - } -} - -static void movLeft2ResultLong(operand *left, int offl, operand *result, - int offr, int sign, int size) { - if (size == 1) - movLeft2Result(left, offl, result, offr, sign); - else { - wassertl(offl == 0 && offr == 0, "Only implemented for zero offset"); - wassertl(size == 2, "Only implemented for two bytes or one"); - - if (IS_GB && requiresHL(AOP(left)) && getPairId(AOP(result)) == PAIR_HL) { - cheapMove(ASMOP_A, 0, AOP(left), LSB); - cheapMove(ASMOP_H, 0, AOP(left), MSB16); - cheapMove(ASMOP_L, 0, ASMOP_A, 0); - spillPair(PAIR_HL); - } else if (getPairId(AOP(result)) == PAIR_IY) { - PAIR_ID id = getPairId(AOP(left)); - if (id != PAIR_INVALID) { - emit2("push %s", _pairs[id].name); - emit2("pop iy"); - regalloc_dry_run_cost += 3; - } else { - fetchPair(PAIR_IY, AOP(left)); + cheapMove(ASMOP_A, 0, AOP(left), offl, true); + cheapMove(AOP(result), offr, ASMOP_A, 0, true); } - } else { - movLeft2Result(left, offl, result, offr, sign); - movLeft2Result(left, offl + 1, result, offr + 1, sign); } } } @@ -2714,15 +3456,11 @@ static void movLeft2ResultLong(operand *left, int offl, operand *result, /** Put Acc into a register set */ static void outAcc(operand *result) { - int size, offset; - size = getDataSize(result); + int size = getDataSize(result); if (size) { - cheapMove(AOP(result), 0, ASMOP_A, 0); + cheapMove(AOP(result), 0, ASMOP_A, 0, true); size--; - offset = 1; - /* unsigned or positive */ - while (size--) - aopPut3(AOP(result), offset++, ASMOP_ZERO, 0); + genMove_o(result->aop, 1, ASMOP_ZERO, 0, size, true, false, false); } } @@ -2734,7 +3472,7 @@ static void outBitC(operand *result) { if (!IS_OP_RUONLY(result) && !regalloc_dry_run) aopPut(AOP(result), "c", 0); // Todo: Cost. } else { - emit2("ld a,!zero"); + emit2("ld a, !zero"); emit2("rla"); regalloc_dry_run_cost += 3; outAcc(result); @@ -2747,19 +3485,40 @@ static void outBitC(operand *result) { static void _toBoolean(const operand *oper, bool needflag) { int size = AOP_SIZE(oper); sym_link *type = operandType(oper); - int offset = size - 1; + int skipbyte; - cheapMove(ASMOP_A, 0, AOP(oper), offset--); - if (size > 1) { - if (IS_FLOAT(type)) { - emit2("res 7, a"); // clear sign bit - regalloc_dry_run_cost += 2; - } - while (--size) - emit3_o(A_OR, ASMOP_A, 0, AOP(oper), offset--); - } else if (needflag) { + if (size == 1 && needflag) { + cheapMove(ASMOP_A, 0, oper->aop, 0, true); emit3(A_OR, ASMOP_A, ASMOP_A); + return; + } + + // Special handling to not overwrite a. + if (oper->aop->regs[A_IDX] >= 0) + skipbyte = oper->aop->regs[A_IDX]; + else { + cheapMove(ASMOP_A, 0, oper->aop, size - 1, true); + skipbyte = size - 1; } + + if (IS_FLOAT(type)) { + if (skipbyte != size - 1) { + wassert(regalloc_dry_run); + regalloc_dry_run_cost += 120; + } + emit2("res 7, a"); // clear sign bit + regalloc_dry_run_cost += 2; + skipbyte = size - 1; + } + while (size--) + if (size != skipbyte) { + if (aopInReg(oper->aop, size, IYL_IDX) || + aopInReg(oper->aop, size, IYH_IDX)) { + regalloc_dry_run_cost += 100; + wassert(regalloc_dry_run); + } + emit3_o(A_OR, ASMOP_A, 0, oper->aop, size); + } } /*-----------------------------------------------------------------*/ @@ -2769,13 +3528,13 @@ static void _castBoolean(const operand *right) { emitDebug("; Casting to bool"); /* Can do without OR-ing for small arguments */ - if (AOP_SIZE(right) == 1 && AOP_TYPE(right) != AOP_ACC) { + if (AOP_SIZE(right) == 1 && !aopInReg(right->aop, 0, A_IDX)) { emit3(A_XOR, ASMOP_A, ASMOP_A); emit3(A_CP, ASMOP_A, AOP(right)); } else { _toBoolean(right, FALSE); - emit2("add a,!immedbyte", 0xff); - emit2("ld a,!zero"); + emit2("add a, !immedbyte", 0xff); + emit2("ld a, !zero"); regalloc_dry_run_cost += 4; } emit2("rla"); @@ -2783,16 +3542,18 @@ static void _castBoolean(const operand *right) { } /* Shuffle src reg array into dst reg array. */ -static void regMove(const short *dst, const short *src, size_t n, - bool preserve_a) { - bool assigned[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; +static void +regMove(const short *dst, const short *src, size_t n, + bool preserve_a) // Todo: replace uses of this one by uses of genMove_o? +{ + bool assigned[9] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; int cached_byte = -1; size_t size = n; int ex[4] = {-1, -1, -1, -1}; size_t i; bool pushed_a = FALSE; - wassert(n < 6); + wassert(n <= 9); // Try to use ex de, hl if (size >= 4) { @@ -2824,6 +3585,7 @@ static void regMove(const short *dst, const short *src, size_t n, // We need to be able to handle any assignment here, ensuring not to overwrite // any parts of the source that we still need. while (size) { + emit2(";size %d", (int)size); // Find lowest byte that can be assigned and needs to be assigned. for (i = 0; i < n; i++) { size_t j; @@ -2843,8 +3605,8 @@ static void regMove(const short *dst, const short *src, size_t n, } if (i < n) { - cheapMove(asmopregs[dst[i]], 0, asmopregs[src[i]], - 0); // We can safely assign a byte. + cheapMove(asmopregs[dst[i]], 0, asmopregs[src[i]], 0, + false); // We can safely assign a byte. size--; assigned[i] = TRUE; continue; @@ -2856,7 +3618,7 @@ static void regMove(const short *dst, const short *src, size_t n, if (cached_byte != -1) { // Already one cached. Can happen when the assignment is a permutation // consisting of multiple cycles. - cheapMove(asmopregs[dst[cached_byte]], 0, ASMOP_A, 0); + cheapMove(asmopregs[dst[cached_byte]], 0, ASMOP_A, 0, true); cached_byte = -1; continue; } @@ -2872,14 +3634,14 @@ static void regMove(const short *dst, const short *src, size_t n, _push(PAIR_AF); pushed_a = TRUE; } - cheapMove(ASMOP_A, 0, asmopregs[src[i]], 0); + cheapMove(ASMOP_A, 0, asmopregs[src[i]], 0, true); size--; assigned[i] = TRUE; cached_byte = i; } if (cached_byte != -1) - cheapMove(asmopregs[dst[cached_byte]], 0, ASMOP_A, 0); + cheapMove(asmopregs[dst[cached_byte]], 0, ASMOP_A, 0, true); if (pushed_a) _pop(PAIR_AF); @@ -2900,10 +3662,28 @@ static void genNot(const iCode *ic) { if (AOP_TYPE(left) == AOP_CRY) { wassertl(0, "Tried to negate a bit"); } else if (IS_BOOL(operandType(left))) { - cheapMove(ASMOP_A, 0, AOP(left), 0); + cheapMove(ASMOP_A, 0, AOP(left), 0, true); emit2("xor a, !immedbyte", 0x01); regalloc_dry_run_cost += 2; - cheapMove(AOP(result), 0, ASMOP_A, 0); + cheapMove(AOP(result), 0, ASMOP_A, 0, true); + goto release; + } else if (IS_RAB && left->aop->size == 2 && aopInReg(left->aop, 0, HL_IDX) && + isPairDead(PAIR_HL, ic) && aopInReg(result->aop, 0, L_IDX)) { + emit2("bool hl"); + emit2("rr hl"); + emit2("ccf"); + emit2("adc hl, hl"); + cost(5, 10); + goto release; + } else if (IS_RAB && left->aop->size == 2 && aopInReg(left->aop, 0, HL_IDX) && + isPairDead(PAIR_HL, ic)) { + emit2("bool hl"); + emit2("xor a, a"); + emit2("rr hl"); + emit2("ccf"); + emit2("rla"); + cost(5, 10); + cheapMove(result->aop, 0, ASMOP_A, 0, true); goto release; } @@ -2927,228 +3707,133 @@ static void genNot(const iCode *ic) { /* genCpl - generate code for complement */ /*-----------------------------------------------------------------*/ static void genCpl(const iCode *ic) { - int offset = 0; - int size; + int skip_byte = -1; + + bool a_dead = !bitVectBitValue(ic->rSurv, A_IDX); + bool pushed_a = false; /* assign asmOps to operand & result */ - aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - aopOp(IC_RESULT(ic), ic, TRUE, FALSE); + aopOp(IC_LEFT(ic), ic, false, false); + aopOp(IC_RESULT(ic), ic, true, false); /* if both are in bit space then a special case */ - if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && AOP_TYPE(IC_LEFT(ic)) == AOP_CRY) { + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && AOP_TYPE(IC_LEFT(ic)) == AOP_CRY) wassertl(0, "Left and the result are in bit space"); - } - size = AOP_SIZE(IC_RESULT(ic)); - while (size--) { - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - emit3(A_CPL, 0, 0); - cheapMove(AOP(IC_RESULT(ic)), offset++, ASMOP_A, 0); - } + int size = IC_RESULT(ic)->aop->size; - /* release the aops */ - freeAsmop(IC_LEFT(ic), NULL); - freeAsmop(IC_RESULT(ic), NULL); -} - -static void _gbz80_emitAddSubLongLong(const iCode *ic, asmop *left, - asmop *right, bool isAdd) { - enum asminst first = isAdd ? A_ADD : A_SUB; - enum asminst later = isAdd ? A_ADC : A_SBC; - - /* Logic: - ld de,right.lw - setup hl to left - de = hl - de - push flags - store de into result - pop flags - ld de,right.hw - setup hl - de = hl -de - store de into result - */ - - wassertl(IS_GB, "Code is only relevant to the gbz80"); - wassertl(AOP(IC_RESULT(ic))->size == 4, "Only works for four bytes"); - - fetchPair(PAIR_DE, left); - - emit2("ld a,e"); - regalloc_dry_run_cost += 1; - emit3_o(first, ASMOP_A, 0, right, LSB); - emit2("ld e,a"); - emit2("ld a,d"); - regalloc_dry_run_cost += 2; - emit3_o(later, ASMOP_A, 0, right, MSB16); - - _push(PAIR_AF); - - cheapMove(AOP(IC_RESULT(ic)), MSB16, ASMOP_A, 0); - cheapMove(AOP(IC_RESULT(ic)), LSB, ASMOP_E, 0); - - fetchPairLong(PAIR_DE, left, NULL, MSB24); - - if (!regalloc_dry_run) - aopGet(right, MSB24, FALSE); - - _pop(PAIR_AF); - emit2("ld a,e"); - emit3_o(later, ASMOP_A, 0, right, MSB24); - emit2("ld e,a"); - emit2("ld a,d"); - regalloc_dry_run_cost += 2; - emit3_o(later, ASMOP_A, 0, right, MSB32); - - cheapMove(AOP(IC_RESULT(ic)), MSB32, ASMOP_A, 0); - cheapMove(AOP(IC_RESULT(ic)), MSB24, ASMOP_E, 0); -} - -static void _gbz80_emitAddSubLong(const iCode *ic, bool isAdd) { - _gbz80_emitAddSubLongLong(ic, AOP(IC_LEFT(ic)), AOP(IC_RIGHT(ic)), isAdd); -} + if (IC_LEFT(ic)->aop->regs[A_IDX] >= 0 && + IC_LEFT(ic)->aop->regs[A_IDX] < size) { + int i = IC_LEFT(ic)->aop->regs[A_IDX]; + emit3(A_CPL, 0, 0); + cheapMove(IC_RESULT(ic)->aop, i, ASMOP_A, 0, true); + skip_byte = i; -/*-----------------------------------------------------------------*/ -/* genUminusFloat - unary minus for floating points */ -/*-----------------------------------------------------------------*/ -static void genUminusFloat(operand *op, operand *result) { - int size, offset = 0; + if (aopInReg(IC_RESULT(ic)->aop, i, A_IDX)) + a_dead = false; - emitDebug("; genUminusFloat"); + // Do not overwrite still-needed value + if (IC_RESULT(ic)->aop->type == AOP_REG && + !aopInReg(IC_RESULT(ic)->aop, i, A_IDX)) { + int j = IC_LEFT(ic)->aop->regs[IC_RESULT(ic)->aop->aopu.aop_reg[i]->rIdx]; + if (j >= 0 && j != skip_byte && j < size) { + regalloc_dry_run_cost += 150; + wassert(regalloc_dry_run); + } + } + } - /* for this we just need to flip the - first bit then copy the rest in place */ - size = AOP_SIZE(op) - 1; + for (int i = 0; i < size; i++) { + if (i == skip_byte) + continue; - cheapMove(ASMOP_A, 0, AOP(op), MSB32); + if (!a_dead && !pushed_a) { + _push(PAIR_AF); + pushed_a = true; + } - emit2("xor a,!immedbyte", 0x80); - regalloc_dry_run_cost += 2; - cheapMove(AOP(result), MSB32, ASMOP_A, 0); + cheapMove(ASMOP_A, 0, IC_LEFT(ic)->aop, i, true); + emit3(A_CPL, 0, 0); + cheapMove(IC_RESULT(ic)->aop, i, ASMOP_A, 0, true); - if (operandsEqu(result, op)) - return; + if (aopInReg(IC_RESULT(ic)->aop, i, A_IDX)) + a_dead = false; - while (size--) { - cheapMove(AOP(result), offset, AOP(op), offset); - offset++; + // Do not overwrite still-needed value + if (IC_RESULT(ic)->aop->type == AOP_REG && + !aopInReg(IC_RESULT(ic)->aop, i, A_IDX)) { + int j = IC_LEFT(ic)->aop->regs[IC_RESULT(ic)->aop->aopu.aop_reg[i]->rIdx]; + if (j > i && j < size && j != skip_byte) { + regalloc_dry_run_cost += 150; + wassert(regalloc_dry_run); + } + } } -} -/*-----------------------------------------------------------------*/ -/* genUminus - unary minus code generation */ -/*-----------------------------------------------------------------*/ -static void genUminus(const iCode *ic) { - int offset, size; - sym_link *optype; - - /* assign asmops */ - aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - aopOp(IC_RESULT(ic), ic, TRUE, FALSE); - - /* if both in bit space then special - case */ - if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && AOP_TYPE(IC_LEFT(ic)) == AOP_CRY) { - wassertl(0, "Left and right are in bit space"); - goto release; - } + if (pushed_a) + _pop(PAIR_AF); - optype = operandType(IC_LEFT(ic)); + /* release the aops */ + freeAsmop(IC_LEFT(ic), 0); + freeAsmop(IC_RESULT(ic), 0); +} - /* if float then do float stuff */ - if (IS_FLOAT(optype)) { - genUminusFloat(IC_LEFT(ic), IC_RESULT(ic)); - goto release; - } +static void _gbz80_emitAddSubLongLong(const iCode *ic, asmop *left, + asmop *right, bool isAdd) { + enum asminst first = isAdd ? A_ADD : A_SUB; + enum asminst later = isAdd ? A_ADC : A_SBC; - /* otherwise subtract from zero */ - size = AOP_SIZE(IC_LEFT(ic)); + /* Logic: + ld de,right.lw + setup hl to left + de = hl - de + push flags + store de into result + pop flags + ld de,right.hw + setup hl + de = hl -de + store de into result + */ - if (AOP_SIZE(IC_RESULT(ic)) == 4 && IS_GB) { - /* Create a new asmop with value zero */ - asmop *azero = newAsmop(AOP_SIMPLELIT); - azero->aopu.aop_simplelit = 0; - azero->size = size; - _gbz80_emitAddSubLongLong(ic, azero, AOP(IC_LEFT(ic)), FALSE); - goto release; - } + wassertl(IS_GB, "Code is only relevant to the gbz80"); + wassertl(AOP(IC_RESULT(ic))->size == 4, "Only works for four bytes"); - offset = 0; + fetchPair(PAIR_DE, left); - if (size == 2 && IS_GB && requiresHL(AOP(IC_RESULT(ic)))) { - cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), 1); - emit3(A_XOR, ASMOP_A, ASMOP_A); - emit3(A_SUB, ASMOP_A, ASMOP_E); - emit3(A_LD, ASMOP_E, ASMOP_A); - emit3(A_LD, ASMOP_A, ASMOP_ZERO); - emit3(A_SBC, ASMOP_A, ASMOP_D); - cheapMove(AOP(IC_RESULT(ic)), 1, ASMOP_A, 0); - cheapMove(AOP(IC_RESULT(ic)), 0, ASMOP_E, 0); - offset = 2; - goto remaining; - } + emit2("ld a, e"); + regalloc_dry_run_cost += 1; + emit3_o(first, ASMOP_A, 0, right, LSB); + emit2("ld e, a"); + emit2("ld a, d"); + regalloc_dry_run_cost += 2; + emit3_o(later, ASMOP_A, 0, right, MSB16); - emit3(A_XOR, ASMOP_A, ASMOP_A); /* Clear accumulator - for first byte. */ - while (size--) { - emit3_o(offset ? A_SBC : A_SUB, ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - _G.preserveCarry = (size ? 1 : 0); - cheapMove(AOP(IC_RESULT(ic)), offset++, ASMOP_A, - 0); /* Uses add iy, sp for AOP_EXSTK when omitting frame pointer, - potentially destroying carry flag. */ - if (size) - emit3(A_LD, ASMOP_A, ASMOP_ZERO); /* Clear accumulator, but not carry - - for subsequent bytes. */ - } - -remaining: - /* if any remaining bytes in the result */ - /* we just need to propagate the sign */ - if ((size = (AOP_SIZE(IC_RESULT(ic)) - AOP_SIZE(IC_LEFT(ic))))) { - emit3(A_RLC, ASMOP_A, 0); - emit3(A_SBC, ASMOP_A, ASMOP_A); - while (size--) - cheapMove(AOP(IC_RESULT(ic)), offset++, ASMOP_A, 0); - } + _push(PAIR_AF); -release: - /* release the aops */ - freeAsmop(IC_LEFT(ic), NULL); - freeAsmop(IC_RESULT(ic), NULL); -} + cheapMove(AOP(IC_RESULT(ic)), MSB16, ASMOP_A, 0, true); + cheapMove(AOP(IC_RESULT(ic)), LSB, ASMOP_E, 0, true); -/*-----------------------------------------------------------------*/ -/* assignResultValue - */ -/*-----------------------------------------------------------------*/ -static void assignResultValue(operand *oper) { - int size = AOP_SIZE(oper); + fetchPairLong(PAIR_DE, left, NULL, MSB24); - wassertl(size <= 4, "Got a result that is bigger than four bytes"); + if (!regalloc_dry_run) + aopGet(right, MSB24, FALSE); - if (IS_GB && size == 4 && requiresHL(AOP(oper))) { - /* We do it the hard way here. */ - _push(PAIR_HL); - cheapMove(AOP(oper), 0, _fReturn3[0], 0); - cheapMove(AOP(oper), 1, _fReturn3[1], 0); - _pop(PAIR_DE); - cheapMove(AOP(oper), 2, _fReturn3[0], 0); - cheapMove(AOP(oper), 3, _fReturn3[1], 0); - } else { - if (AOP_TYPE(oper) == AOP_REG) { - int i; - short retarray[4], oparray[4]; + _pop(PAIR_AF); + emit2("ld a, e"); + emit3_o(later, ASMOP_A, 0, right, MSB24); + emit2("ld e, a"); + emit2("ld a, d"); + regalloc_dry_run_cost += 2; + emit3_o(later, ASMOP_A, 0, right, MSB32); - for (i = 0; i < size; i++) { - retarray[i] = _fReturn3[i]->aopu.aop_reg[0]->rIdx; - oparray[i] = AOP(oper)->aopu.aop_reg[i]->rIdx; - } + cheapMove(AOP(IC_RESULT(ic)), MSB32, ASMOP_A, 0, true); + cheapMove(AOP(IC_RESULT(ic)), MSB24, ASMOP_E, 0, true); +} - regMove(oparray, retarray, size, FALSE); - } else - while (size--) - cheapMove(AOP(oper), size, _fReturn3[size], 0); - } +static void _gbz80_emitAddSubLong(const iCode *ic, bool isAdd) { + _gbz80_emitAddSubLongLong(ic, AOP(IC_LEFT(ic)), AOP(IC_RIGHT(ic)), isAdd); } /* Pop saved regs from stack, taking care not to destroy result */ @@ -3185,65 +3870,64 @@ static void restoreRegs(bool iy, bool de, bool bc, bool hl, _pop(PAIR_IY); if (de) { - if (dInRet && eInRet) { + if (dInRet && eInRet) wassertl(0, "Shouldn't push DE if it's wiped out by the return"); - } else if (dInRet) { + else if (dInRet) { /* Only restore E */ - emit2("ld a,d"); + emit2("ld a, d"); regalloc_dry_run_cost += 1; _pop(PAIR_DE); - emit2("ld d,a"); + emit2("ld d, a"); regalloc_dry_run_cost += 1; } else if (eInRet) { /* Only restore D */ _pop(PAIR_AF); - emit2("ld d,a"); + emit2("ld d, a"); regalloc_dry_run_cost += 1; } else _pop(PAIR_DE); } if (bc) { - if (bInRet && cInRet) { + if (bInRet && cInRet) wassertl(0, "Shouldn't push BC if it's wiped out by the return"); - } else if (bInRet) { + else if (bInRet) { /* Only restore C */ - emit2("ld a,b"); + emit2("ld a, b"); regalloc_dry_run_cost += 1; _pop(PAIR_BC); - emit2("ld b,a"); + emit2("ld b, a"); regalloc_dry_run_cost += 1; } else if (cInRet) { /* Only restore B */ _pop(PAIR_AF); - emit2("ld b,a"); + emit2("ld b, a"); regalloc_dry_run_cost += 1; } else _pop(PAIR_BC); } if (hl) { - if (hInRet && lInRet) { + if (hInRet && lInRet) wassertl(0, "Shouldn't push HL if it's wiped out by the return"); - } else if (hInRet) { + else if (hInRet) { /* Only restore E */ - emit2("ld a,h"); + emit2("ld a, h"); regalloc_dry_run_cost += 1; _pop(PAIR_HL); - emit2("ld h,a"); + emit2("ld h, a"); regalloc_dry_run_cost += 1; } else if (lInRet) { /* Only restore D */ _pop(PAIR_AF); - emit2("ld h,a"); + emit2("ld h, a"); regalloc_dry_run_cost += 1; } else _pop(PAIR_HL); } } -static void _saveRegsForCall(const iCode *ic, int sendSetSize, - bool dontsaveIY) { +static void _saveRegsForCall(const iCode *ic, bool dontsaveIY) { /* Rules: o Stack parameters are pushed before this function enters o DE and BC may be used in this function. @@ -3266,12 +3950,14 @@ static void _saveRegsForCall(const iCode *ic, int sendSetSize, o ... */ + sym_link *dtype = operandType(IC_LEFT(ic)); + sym_link *ftype = IS_FUNCPTR(dtype) ? dtype->next : dtype; + if (_G.saves.saved == FALSE) { bool push_bc, push_de, push_hl, push_iy; if (options.oldralloc) { bool deInUse, bcInUse; - bool deSending; bool bcInRet = FALSE, deInRet = FALSE; bitVect *rInUse; @@ -3283,21 +3969,22 @@ static void _saveRegsForCall(const iCode *ic, int sendSetSize, bcInUse = bitVectBitValue(rInUse, B_IDX) || bitVectBitValue(rInUse, C_IDX); - deSending = (sendSetSize > 1); - - emitDebug("; _saveRegsForCall: sendSetSize: %u deInUse: %u bcInUse: %u " - "deSending: %u", - sendSetSize, deInUse, bcInUse, deSending); + emitDebug("; _saveRegsForCall: deInUse: %u bcInUse: %u", deInUse, + bcInUse); push_bc = bcInUse && !bcInRet; push_de = deInUse && !deInRet; push_hl = FALSE; push_iy = FALSE; } else { - push_bc = bitVectBitValue(ic->rSurv, B_IDX) || - bitVectBitValue(ic->rSurv, C_IDX); - push_de = bitVectBitValue(ic->rSurv, D_IDX) || - bitVectBitValue(ic->rSurv, E_IDX); + push_bc = bitVectBitValue(ic->rSurv, B_IDX) && + !ftype->funcAttrs.preserved_regs[B_IDX] || + bitVectBitValue(ic->rSurv, C_IDX) && + !ftype->funcAttrs.preserved_regs[C_IDX]; + push_de = bitVectBitValue(ic->rSurv, D_IDX) && + !ftype->funcAttrs.preserved_regs[D_IDX] || + bitVectBitValue(ic->rSurv, E_IDX) && + !ftype->funcAttrs.preserved_regs[E_IDX]; push_hl = bitVectBitValue(ic->rSurv, H_IDX) || bitVectBitValue(ic->rSurv, L_IDX); push_iy = !dontsaveIY && (bitVectBitValue(ic->rSurv, IYH_IDX) || @@ -3332,8 +4019,6 @@ static void _saveRegsForCall(const iCode *ic, int sendSetSize, /* genIpush - genrate code for pushing this gets a little complex */ /*-----------------------------------------------------------------*/ static void genIpush(const iCode *ic) { - int size, offset = 0; - /* if this is not a parm push : ie. it is spill push and spill push is always done on the local stack */ if (!ic->parmPush) { @@ -3341,143 +4026,237 @@ static void genIpush(const iCode *ic) { return; } - if (_G.saves.saved == FALSE && - !regalloc_dry_run /* Cost is counted at CALL or PCALL instead */) { - /* Caller saves, and this is the first iPush. */ - /* Scan ahead until we find the function that we are pushing parameters to. - Count the number of addSets on the way to figure out what registers - are used in the send set. - */ - int nAddSets = 0; - iCode *walk = ic->next; + /* Scan ahead until we find the function that we are pushing parameters to. + Count the number of addSets on the way to figure out what registers + are used in the send set. + */ + int nAddSets = 0; + iCode *walk = ic->next; - while (walk) { - if (walk->op == SEND) { - nAddSets++; - } else if (walk->op == CALL || walk->op == PCALL) { - /* Found it. */ - break; - } else { - /* Keep looking. */ - } - walk = walk->next; - } - _saveRegsForCall(walk, nAddSets, FALSE); + while (walk) { + if (walk->op == SEND && !_G.saves.saved && !regalloc_dry_run) + nAddSets++; + else if (walk->op == CALL || walk->op == PCALL) + break; // Found it. + + walk = walk->next; // Keep looking. } + if (!regalloc_dry_run && !_G.saves.saved && + !regalloc_dry_run) /* Cost is counted at CALL or PCALL instead */ + _saveRegsForCall(walk, + false); /* Caller saves, and this is the first iPush. */ + + const bool smallc = IFFUNC_ISSMALLC(operandType(IC_LEFT(walk))); /* then do the push */ aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - size = AOP_SIZE(IC_LEFT(ic)); + int size = AOP_SIZE(IC_LEFT(ic)); - if (isPair(AOP(IC_LEFT(ic))) && size == 2) { - if (!regalloc_dry_run) { + if (size == 1 && smallc) /* The SmallC calling convention pushes 8-bit + parameters as 16-bit values. */ + { + if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && + AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX) { + emit2("push bc"); + regalloc_dry_run_cost++; + } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && + AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX) { + emit2("push de"); + regalloc_dry_run_cost++; + } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && + AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == L_IDX) { + emit2("push hl"); + regalloc_dry_run_cost++; + } else if (!bitVectBitValue(ic->rSurv, A_IDX)) { + emit2("dec sp"); + cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), 0, true); + emit2("push af"); + emit2("inc sp"); + regalloc_dry_run_cost += 3; + } else if (!IS_GB) { + emit2("push hl"); + cheapMove(ASMOP_L, 0, AOP(IC_LEFT(ic)), 0, false); + emit2("ex (sp), hl"); + spillPair(PAIR_HL); + regalloc_dry_run_cost += 2; + } else + wassert(0); + + if (!regalloc_dry_run) _G.stack.pushed += 2; - emit2("push %s", getPairName(AOP(IC_LEFT(ic)))); - } - regalloc_dry_run_cost += (getPairId(AOP(IC_LEFT(ic))) == PAIR_IY ? 2 : 1); - } else { - if (size == 2) { - PAIR_ID pair = getDeadPairId(ic); - if (pair == PAIR_INVALID || isPairDead(PAIR_HL, ic)) - pair = PAIR_HL; /* hl sometimes is cheaper to load than other pairs. */ + goto release; + } - fetchPairLong(pair, AOP(IC_LEFT(ic)), ic, 0); - if (!regalloc_dry_run) { - emit2("push %s", _pairs[pair].name); - _G.stack.pushed += 2; - } - regalloc_dry_run_cost += 1; - goto release; - } - if (size == 4) { - if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[2]->rIdx == C_IDX && - AOP(IC_LEFT(ic))->aopu.aop_reg[3]->rIdx == B_IDX) { - emit2("push bc"); - regalloc_dry_run_cost += 1; - } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[2]->rIdx == E_IDX && - AOP(IC_LEFT(ic))->aopu.aop_reg[3]->rIdx == D_IDX) { - emit2("push de"); - regalloc_dry_run_cost += 1; - } else { - fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, 2); - emit2("push hl"); - regalloc_dry_run_cost += 1; - } - if (!regalloc_dry_run) - _G.stack.pushed += 2; + while (size) { + int d = 0; + + bool a_free = !bitVectBitValue(ic->rSurv, A_IDX) && + (IC_LEFT(ic)->aop->regs[A_IDX] < 0 || + IC_LEFT(ic)->aop->regs[A_IDX] >= size - 1); + bool b_free = !bitVectBitValue(ic->rSurv, B_IDX) && + (IC_LEFT(ic)->aop->regs[B_IDX] < 0 || + IC_LEFT(ic)->aop->regs[B_IDX] >= size - 1); + bool c_free = !bitVectBitValue(ic->rSurv, C_IDX) && + (IC_LEFT(ic)->aop->regs[C_IDX] < 0 || + IC_LEFT(ic)->aop->regs[C_IDX] >= size - 1); + bool d_free = !bitVectBitValue(ic->rSurv, D_IDX) && + (IC_LEFT(ic)->aop->regs[D_IDX] < 0 || + IC_LEFT(ic)->aop->regs[D_IDX] >= size - 1); + bool e_free = !bitVectBitValue(ic->rSurv, E_IDX) && + (IC_LEFT(ic)->aop->regs[E_IDX] < 0 || + IC_LEFT(ic)->aop->regs[E_IDX] >= size - 1); + bool h_free = !bitVectBitValue(ic->rSurv, H_IDX) && + (IC_LEFT(ic)->aop->regs[H_IDX] < 0 || + IC_LEFT(ic)->aop->regs[H_IDX] >= size - 1); + bool l_free = !bitVectBitValue(ic->rSurv, L_IDX) && + (IC_LEFT(ic)->aop->regs[L_IDX] < 0 || + IC_LEFT(ic)->aop->regs[L_IDX] >= size - 1); + bool hl_free = isPairDead(PAIR_HL, ic) && + (h_free || IC_LEFT(ic)->aop->regs[H_IDX] >= size - 2) && + (l_free || IC_LEFT(ic)->aop->regs[L_IDX] >= size - 2); + bool de_free = isPairDead(PAIR_DE, ic) && + (d_free || IC_LEFT(ic)->aop->regs[D_IDX] >= size - 2) && + (e_free || IC_LEFT(ic)->aop->regs[E_IDX] >= size - 2); + bool bc_free = isPairDead(PAIR_BC, ic) && + (b_free || IC_LEFT(ic)->aop->regs[B_IDX] >= size - 2) && + (c_free || IC_LEFT(ic)->aop->regs[C_IDX] >= size - 2); + + if (getPairId_o(IC_LEFT(ic)->aop, size - 2) != PAIR_INVALID) { + emit2("push %s", _pairs[getPairId_o(IC_LEFT(ic)->aop, size - 2)].name); + regalloc_dry_run_cost += + 1 + (getPairId_o(IC_LEFT(ic)->aop, 2) == PAIR_IY); + d = 2; + } else if (size >= 2 && + (hl_free || de_free || bc_free || + aopInReg(IC_LEFT(ic)->aop, size - 1, B_IDX) && c_free || + b_free && aopInReg(IC_LEFT(ic)->aop, size - 2, C_IDX) || + aopInReg(IC_LEFT(ic)->aop, size - 1, D_IDX) && e_free || + d_free && aopInReg(IC_LEFT(ic)->aop, size - 2, E_IDX) || + aopInReg(IC_LEFT(ic)->aop, size - 1, H_IDX) && l_free || + h_free && aopInReg(IC_LEFT(ic)->aop, size - 2, L_IDX))) { + PAIR_ID pair = PAIR_INVALID; + + if (hl_free) + pair = PAIR_HL; + else if (de_free) + pair = PAIR_DE; + else if (bc_free) + pair = PAIR_BC; - if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && - AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == B_IDX) { - emit2("push bc"); - regalloc_dry_run_cost += 1; - } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && - AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == D_IDX) { - emit2("push de"); - regalloc_dry_run_cost += 1; - } else { - fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, 0); - emit2("push hl"); - regalloc_dry_run_cost += 1; - } - if (!regalloc_dry_run) - _G.stack.pushed += 2; + if (aopInReg(IC_LEFT(ic)->aop, size - 1, H_IDX) && l_free || + h_free && aopInReg(IC_LEFT(ic)->aop, size - 2, L_IDX)) + pair = PAIR_HL; + else if (aopInReg(IC_LEFT(ic)->aop, size - 1, D_IDX) && e_free || + d_free && aopInReg(IC_LEFT(ic)->aop, size - 2, E_IDX)) + pair = PAIR_DE; + else if (aopInReg(IC_LEFT(ic)->aop, size - 1, B_IDX) && c_free || + b_free && aopInReg(IC_LEFT(ic)->aop, size - 2, C_IDX)) + pair = PAIR_BC; - goto release; - } - offset = size; - while (size--) { - if (AOP(IC_LEFT(ic))->type == AOP_IY) { - wassertl(!bitVectBitValue(ic->rSurv, A_IDX), - "Loading from address destroys A, which must survive."); - emit2("ld a,(%s)", - aopGetLitWordLong(AOP(IC_LEFT(ic)), --offset, FALSE)); - emit2("push af"); - regalloc_dry_run_cost += 4; - } else { - offset--; - if (AOP(IC_LEFT(ic))->type == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[offset]->rIdx == B_IDX) { - emit2("push bc"); - regalloc_dry_run_cost += 1; - } else if (AOP(IC_LEFT(ic))->type == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[offset]->rIdx == D_IDX) { - emit2("push de"); - regalloc_dry_run_cost += 1; - } else if (AOP(IC_LEFT(ic))->type == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[offset]->rIdx == H_IDX) { - emit2("push hl"); - regalloc_dry_run_cost += 1; - } else { - if (!regalloc_dry_run && - !strcmp(aopGet(AOP(IC_LEFT(ic)), offset, FALSE), - "h")) // todo: More exact cost! - emit2("push hl"); - else { - wassertl(AOP_TYPE(IC_LEFT(ic)) == AOP_ACC || - !bitVectBitValue(ic->rSurv, A_IDX), - "Push operand destroys A, which must survive."); - if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && - byteOfVal(AOP(IC_LEFT(ic))->aopu.aop_lit, offset) == 0x00) - emit3(A_XOR, ASMOP_A, ASMOP_A); - else - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - emit2("push af"); - } - regalloc_dry_run_cost += (ld_cost(ASMOP_A, AOP(IC_LEFT(ic))) + 1); - } - } - if (!regalloc_dry_run) { - emit2("inc sp"); - _G.stack.pushed++; - } - regalloc_dry_run_cost += 1; - } + fetchPairLong(pair, IC_LEFT(ic)->aop, ic, size - 2); + emit2("push %s", _pairs[pair].name); + regalloc_dry_run_cost++; + d = 2; + } else if (size >= 2 && IS_Z80N && + (IC_LEFT(ic)->aop->type == AOP_LIT || + IC_LEFT(ic)->aop->type == + AOP_IMMD)) // Same size, but slower (21 vs 23 cycles) than + // going through a register pair other than iy. + // Only worth it under high register pressure. + { + emit2("push !hashedstr", + aopGetLitWordLong(IC_LEFT(ic)->aop, size - 2, false)); + regalloc_dry_run_cost += 4; + d = 2; + } else if (size >= 2 && !IS_GB && !IY_RESERVED && isPairDead(PAIR_IY, ic) && + (IC_LEFT(ic)->aop->type == AOP_LIT || + IC_LEFT(ic)->aop->type == AOP_IMMD)) { + fetchPairLong(PAIR_IY, IC_LEFT(ic)->aop, ic, size - 2); + emit2("push iy"); + regalloc_dry_run_cost += 2; + d = 2; + } else if (size >= 2 && !IS_GB) { + emit2("push hl"); + _G.stack.pushed += 2; + fetchPairLong(PAIR_HL, IC_LEFT(ic)->aop, ic, size - 2); + _G.stack.pushed -= 2; + emit2("ex (sp), hl"); + spillPair(PAIR_HL); + regalloc_dry_run_cost += 3; + d = 2; + } else if (aopInReg(IC_LEFT(ic)->aop, size - 1, A_IDX)) { + emit2("push af"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (aopInReg(IC_LEFT(ic)->aop, size - 1, B_IDX)) { + emit2("push bc"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (aopInReg(IC_LEFT(ic)->aop, size - 1, D_IDX)) { + emit2("push de"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (aopInReg(IC_LEFT(ic)->aop, size - 1, H_IDX)) { + emit2("push hl"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (aopInReg(IC_LEFT(ic)->aop, size - 1, IYH_IDX)) { + emit2("push iy"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (a_free) { + genMove_o(ASMOP_A, 0, IC_LEFT(ic)->aop, size - 1, 1, true, + h_free && l_free, d_free && e_free); + emit2("push af"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (h_free) { + cheapMove(ASMOP_H, 0, IC_LEFT(ic)->aop, size - 1, false); + emit2("push hl"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (d_free) { + cheapMove(ASMOP_D, 0, IC_LEFT(ic)->aop, size - 1, false); + emit2("push de"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (b_free) { + cheapMove(ASMOP_B, 0, IC_LEFT(ic)->aop, size - 1, false); + emit2("push bc"); + emit2("inc sp"); + regalloc_dry_run_cost += 2; + d = 1; + } else if (IS_Z80N && IC_LEFT(ic)->aop->type == AOP_LIT) { + emit2("push !immedword", + byteOfVal(IC_LEFT(ic)->aop->aopu.aop_lit, size - 1) << 8); + emit2("inc sp"); + regalloc_dry_run_cost += 5; + d = 1; + } else if (!IS_GB) { + emit2("push hl"); + cheapMove(ASMOP_H, 0, IC_LEFT(ic)->aop, size - 1, false); + emit2("ex (sp), hl"); + spillPair(PAIR_HL); + emit2("inc sp"); + regalloc_dry_run_cost += 3; + d = 1; + } else + wassert(0); + + if (!regalloc_dry_run) + _G.stack.pushed += d; + size -= d; } + release: freeAsmop(IC_LEFT(ic), NULL); } @@ -3531,136 +4310,100 @@ static void setArea(int inHome) { static bool isInHome(void) { return _G.in_home; } -static int _opUsesPair(operand *op, const iCode *ic, PAIR_ID pairId) { - int ret = 0; - asmop *aop; - symbol *sym = OP_SYMBOL(op); +/** Emit the code for a register parameter + */ +static void genSend(const iCode *ic) { + aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - if (sym->isspilt || sym->nRegs == 0) - return 0; + wassertl(ic->next->op == CALL || ic->next->op == PCALL, + "Sending register parameter for missing call"); + wassertl(!IS_GB, "Register parameters are not supported in gbz80 port"); - aopOp(op, ic, FALSE, FALSE); + if (_G.saves.saved == FALSE && + !regalloc_dry_run /* Cost is counted at CALL or PCALL instead */) { + /* Caller saves, and this is the first iPush. */ + /* Scan ahead until we find the function that we are pushing parameters to. + Count the number of addSets on the way to figure out what registers + are used in the send set. + */ + int nAddSets = 0; + iCode *walk = ic->next; - aop = AOP(op); - if (aop->type == AOP_REG) { - int i; - for (i = 0; i < aop->size; i++) { - if (pairId == PAIR_DE) { - emitDebug("; name %s", aop->aopu.aop_reg[i]->name); - if (!strcmp(aop->aopu.aop_reg[i]->name, "e")) - ret++; - if (!strcmp(aop->aopu.aop_reg[i]->name, "d")) - ret++; - } else if (pairId == PAIR_BC) { - emitDebug("; name %s", aop->aopu.aop_reg[i]->name); - if (!strcmp(aop->aopu.aop_reg[i]->name, "c")) - ret++; - if (!strcmp(aop->aopu.aop_reg[i]->name, "b")) - ret++; + while (walk) { + if (walk->op == SEND) { + nAddSets++; + } else if (walk->op == CALL || walk->op == PCALL) { + /* Found it. */ + break; } else { - wassert(0); + /* Keep looking. */ } + walk = walk->next; } + _saveRegsForCall(walk, FALSE); } + genMove_o(ASMOP_RETURN, 0, IC_LEFT(ic)->aop, 0, IC_LEFT(ic)->aop->size, + !bitVectBitValue(ic->rSurv, A_IDX), isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); + + for (int i = 0; i < IC_LEFT(ic)->aop->size; i++) + if (!regalloc_dry_run) + z80_regs_used_as_parms_in_calls_from_current_function + [ASMOP_RETURN->aopu.aop_reg[i]->rIdx] = true; + freeAsmop(IC_LEFT(ic), NULL); - return ret; } /** Emit the code for a call statement */ -static void emitCall(const iCode *ic, bool ispcall) { - bool SomethingReturned, bigreturn; +static void genCall(const iCode *ic) { sym_link *dtype = operandType(IC_LEFT(ic)); sym_link *etype = getSpec(dtype); sym_link *ftype = IS_FUNCPTR(dtype) ? dtype->next : dtype; + int i; + int prestackadjust = 0; + bool tailjump = false; - _saveRegsForCall(ic, _G.sendSet ? elementsInSet(_G.sendSet) : 0, FALSE); - - /* if send set is not empty then assign */ - if (_G.sendSet) { - iCode *sic; - int send = 0; - int nSend = elementsInSet(_G.sendSet); - bool swapped = FALSE; - - int _z80_sendOrder[] = {PAIR_BC, PAIR_DE}; - - if (nSend > 1) { - /* Check if the parameters are swapped. If so route through hl instead. - */ - wassertl( - nSend == 2, - "Pedantic check. Code only checks for the two send items case."); - - sic = setFirstItem(_G.sendSet); - sic = setNextItem(_G.sendSet); - - if (_opUsesPair(IC_LEFT(sic), sic, _z80_sendOrder[0])) { - /* The second send value is loaded from one the one that holds the first - send, i.e. it is overwritten. */ - /* Cache the first in HL, and load the second from HL instead. */ - emit2("ld h,%s", _pairs[_z80_sendOrder[0]].h); - emit2("ld l,%s", _pairs[_z80_sendOrder[0]].l); - regalloc_dry_run_cost += 2; - - swapped = TRUE; - } - } + const bool z88dk_callee = IFFUNC_ISZ88DK_CALLEE(ftype); - for (sic = setFirstItem(_G.sendSet); sic; sic = setNextItem(_G.sendSet)) { - int size; - aopOp(IC_LEFT(sic), sic, FALSE, FALSE); + for (i = 0; i < IYH_IDX + 1; i++) + z80_regs_preserved_in_calls_from_current_function[i] |= + ftype->funcAttrs.preserved_regs[i]; - size = AOP_SIZE(IC_LEFT(sic)); - wassertl(size <= 2, - "Tried to send a parameter that is bigger than two bytes"); - wassertl(_z80_sendOrder[send] != PAIR_INVALID, - "Tried to send more parameters than we have registers for"); + _saveRegsForCall(ic, FALSE); - // PENDING: Mild hack - if (swapped == TRUE && send == 1) { - if (size > 1) { - emit2("ld %s,h", _pairs[_z80_sendOrder[send]].h); - regalloc_dry_run_cost += 1; - } else { - emit2("ld %s,!zero", _pairs[_z80_sendOrder[send]].h); - regalloc_dry_run_cost += 2; - } - emit2("ld %s,l", _pairs[_z80_sendOrder[send]].l); - regalloc_dry_run_cost += 1; - } else { - fetchPair(_z80_sendOrder[send], AOP(IC_LEFT(sic))); - } + const bool bigreturn = + (getSize(ftype->next) > + 4); // Return value of big type or returning struct or union. + const bool SomethingReturned = + (IS_ITEMP(IC_RESULT(ic)) && + (OP_SYMBOL(IC_RESULT(ic))->nRegs || OP_SYMBOL(IC_RESULT(ic))->spildir || + OP_SYMBOL(IC_RESULT(ic))->accuse == ACCUSE_A)) || + IS_TRUE_SYMOP(IC_RESULT(ic)); - send++; - freeAsmop(IC_LEFT(sic), NULL); - } - _G.sendSet = NULL; - } + aopOp(IC_LEFT(ic), ic, false, false); + if (SomethingReturned && !bigreturn) + aopOp(IC_RESULT(ic), ic, false, false); - /* Return value of big type or returning struct or union. */ - bigreturn = (getSize(ftype->next) > 4); if (bigreturn) { PAIR_ID pair; int fp_offset, sp_offset; - if (ispcall && IS_GB) + if (ic->op == PCALL && IS_GB) _push(PAIR_HL); aopOp(IC_RESULT(ic), ic, FALSE, FALSE); - wassertl(IC_RESULT(ic), - "Unused return value in call to function returning large type."); wassert(AOP_TYPE(IC_RESULT(ic)) == AOP_STK || AOP_TYPE(IC_RESULT(ic)) == AOP_EXSTK); fp_offset = - AOP(IC_RESULT(ic))->aopu.aop_stk + _G.stack.offset + + AOP(IC_RESULT(ic))->aopu.aop_stk + (AOP(IC_RESULT(ic))->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - sp_offset = fp_offset + _G.stack.pushed; - pair = (ispcall && !IS_GB) ? PAIR_IY : PAIR_HL; - emit2("ld %s,!immedword", _pairs[pair].name, sp_offset); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + pair = (ic->op == PCALL && !IS_GB && !IY_RESERVED) ? PAIR_IY : PAIR_HL; + emit2("ld %s, !immedword", _pairs[pair].name, sp_offset); emit2("add %s, sp", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IY ? 6 : 4); - if (ispcall && IS_GB) { + if (ic->op == PCALL && IS_GB) { emit2("ld e, l"); emit2("ld d, h"); regalloc_dry_run_cost += 2; @@ -3673,58 +4416,195 @@ static void emitCall(const iCode *ic, bool ispcall) { _G.stack.pushed += 2; freeAsmop(IC_RESULT(ic), NULL); } + // Check if we can do tail call optimization. + else if (!(currFunc && IFFUNC_ISISR(currFunc->type)) && + (!SomethingReturned || + IC_RESULT(ic)->aop->size == 1 && + aopInReg(IC_RESULT(ic)->aop, 0, IS_GB ? E_IDX : L_IDX) || + IC_RESULT(ic)->aop->size == 2 && + aopInReg(IC_RESULT(ic)->aop, 0, IS_GB ? DE_IDX : HL_IDX)) && + !ic->parmBytes && !ic->localEscapeAlive && + !IFFUNC_ISBANKEDCALL(dtype) && !IFFUNC_ISZ88DK_SHORTCALL(ftype) && + _G.omitFramePtr && + (ic->op != PCALL || !IFFUNC_ISZ88DK_FASTCALL(ftype))) { + int limit = 16; // Avoid endless loops in the code putting us into an + // endless loop here. + + for (const iCode *nic = ic->next; nic && --limit;) { + const symbol *targetlabel = 0; + + if (nic->op == LABEL) + ; + else if (nic->op == + GOTO) // We dont have ebbi here, so we cant jsut use + // eBBWithEntryLabel (ebbi, ic->label). Search manually. + targetlabel = IC_LABEL(nic); + else if (nic->op == RETURN && + (!IC_LEFT(nic) || + SomethingReturned && IC_RESULT(ic)->key == IC_LEFT(nic)->key)) + targetlabel = returnLabel; + else if (nic->op == ENDFUNCTION) { + if (OP_SYMBOL(IC_LEFT(nic))->stack <= + (ic->op == PCALL ? 1 : (optimize.codeSize ? 1 : 2)) + + IS_RAB * 120) { + prestackadjust = OP_SYMBOL(IC_LEFT(nic))->stack; + tailjump = true; + } + break; + } else + break; + + if (targetlabel) { + const iCode *nnic = 0; + for (nnic = nic->next; nnic; nnic = nnic->next) + if (nnic->op == LABEL && IC_LABEL(nnic)->key == targetlabel->key) + break; + if (!nnic) + for (nnic = nic->prev; nnic; nnic = nnic->prev) + if (nnic->op == LABEL && IC_LABEL(nnic)->key == targetlabel->key) + break; + if (!nnic) + break; + + nic = nnic; + } else + nic = nic->next; + } + } + + const bool jump = tailjump || !ic->parmBytes && !bigreturn && + ic->op != PCALL && + !IFFUNC_ISBANKEDCALL(dtype) && + !IFFUNC_ISZ88DK_SHORTCALL(ftype) && + IFFUNC_ISNORETURN(ftype); - if (ispcall) { - aopOp(IC_LEFT(ic), ic, FALSE, FALSE); + if (ic->op == PCALL) { + if (IFFUNC_ISBANKEDCALL(dtype)) { + werror(W_INDIR_BANKED); + } else if (IFFUNC_ISZ88DK_SHORTCALL(ftype)) { + wassertl(0, "__z88dk_short_call via function pointer not implemented"); + } if (isLitWord(AOP(IC_LEFT(ic)))) { - emit2("call %s", aopGetLitWordLong(AOP(IC_LEFT(ic)), 0, FALSE)); + adjustStack(prestackadjust, false, false, false, false); + emit2(jump ? "jp %s" : "call %s", + aopGetLitWordLong(AOP(IC_LEFT(ic)), 0, FALSE)); regalloc_dry_run_cost += 3; - } else { + } else if (getPairId(AOP(IC_LEFT(ic))) != PAIR_IY && + !IFFUNC_ISZ88DK_FASTCALL(ftype)) { spillPair(PAIR_HL); fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), ic, 0); - emit2("call __sdcc_call_hl"); + adjustStack(prestackadjust, false, false, false, false); + emit2(jump ? "jp (hl)" : "call ___sdcc_call_hl"); + regalloc_dry_run_cost += 3; + } else if (!IS_GB && !IY_RESERVED) { + spillPair(PAIR_IY); + fetchPairLong(PAIR_IY, IC_LEFT(ic)->aop, ic, 0); + adjustStack(prestackadjust, false, false, false, false); + emit2(jump ? "jp (iy)" : "call ___sdcc_call_iy"); + regalloc_dry_run_cost += 3; + } else // Use bc, since it is the only 16-bit register guarateed to be free + // even for __z88dk_fastcall with --reserve-regs-iy + { + wassert(!prestackadjust); + wassert(IY_RESERVED); // The peephole optimizer handles ret for purposes + // other than returning only for --reserve-regs-iy + if (aopInReg(IC_LEFT(ic)->aop, 0, B_IDX) || + aopInReg(IC_LEFT(ic)->aop, 0, C_IDX) || + aopInReg(IC_LEFT(ic)->aop, 1, B_IDX) || + aopInReg(IC_LEFT(ic)->aop, 1, C_IDX)) { + regalloc_dry_run_cost += 100; + wassertl(regalloc_dry_run, "Unimplemented function pointer in bc"); + } + symbol *tlbl = 0; + if (!regalloc_dry_run) { + tlbl = newiTempLabel(NULL); + emit2("ld bc, #!tlabel", labelKey2num(tlbl->key)); + emit2("push bc"); + regalloc_dry_run_cost += 4; + } + fetchPairLong(PAIR_BC, IC_LEFT(ic)->aop, 0, 0); + emit2("push bc"); + emit2("ret"); + regalloc_dry_run_cost += 2; + if (tlbl) + emitLabel(tlbl); } - freeAsmop(IC_LEFT(ic), NULL); } else { - - if (IS_LITERAL(etype)) { - emit2("call 0x%04X", ulFromVal(OP_VALUE(IC_LEFT(ic)))); - regalloc_dry_run_cost += 3; + /* make the call */ + if (IFFUNC_ISBANKEDCALL(dtype)) { + wassert(!prestackadjust); + + char *name = OP_SYMBOL(IC_LEFT(ic))->rname[0] + ? OP_SYMBOL(IC_LEFT(ic))->rname + : OP_SYMBOL(IC_LEFT(ic))->name; + /* there 3 types of banked call: + legacy - only if --legacy-banking is specified + a:bc - only for __z88dk_fastcall __banked functions + e:hl - default (may have optimal bank switch routine) */ + if (z80_opts.legacyBanking) { + emit2("call ___sdcc_bcall"); + emit2("!dws", name); + emit2("!dw !bankimmeds", name); + regalloc_dry_run_cost += 7; + } else if (IFFUNC_ISZ88DK_FASTCALL(ftype)) { + spillPair(PAIR_BC); + emit2("ld a, !hashedbankimmeds", name); + emit2("ld bc, !hashedstr", name); + emit2("call ___sdcc_bcall_abc"); + regalloc_dry_run_cost += 8; + } else { + spillPair(PAIR_DE); + spillPair(PAIR_HL); + emit2("ld e, !hashedbankimmeds", name); + emit2("ld hl, !hashedstr", name); + emit2("call ___sdcc_bcall_ehl"); + regalloc_dry_run_cost += 8; + } } else { - bool jump = - (!ic->parmBytes && IFFUNC_ISNORETURN(OP_SYMBOL(IC_LEFT(ic))->type)); - emit2("%s %s", jump ? "jp" : "call", - (OP_SYMBOL(IC_LEFT(ic))->rname[0] ? OP_SYMBOL(IC_LEFT(ic))->rname - : OP_SYMBOL(IC_LEFT(ic))->name)); - regalloc_dry_run_cost += 3; + adjustStack(prestackadjust, false, false, false, false); + + if (IS_LITERAL(etype)) { + emit2(jump ? "jp 0x%04X" : "call 0x%04X", + ulFromVal(OP_VALUE(IC_LEFT(ic)))); + regalloc_dry_run_cost += 3; + } else if (IFFUNC_ISZ88DK_SHORTCALL(ftype)) { + int rst = ftype->funcAttrs.z88dk_shortcall_rst; + int value = ftype->funcAttrs.z88dk_shortcall_val; + emit2("rst 0x%02x", rst); + if (value < 256) + emit2("defb 0x%02x\n", value); + else + emit2("defw 0x%04x\n", value); + regalloc_dry_run_cost += 3; + } else { + emit2("%s %s", jump ? "jp" : "call", + (OP_SYMBOL(IC_LEFT(ic))->rname[0] + ? OP_SYMBOL(IC_LEFT(ic))->rname + : OP_SYMBOL(IC_LEFT(ic))->name)); + regalloc_dry_run_cost += 3; + } } } spillCached(); + freeAsmop(IC_LEFT(ic), 0); + + _G.stack.pushed += prestackadjust; + /* Mark the registers as restored. */ _G.saves.saved = FALSE; - SomethingReturned = - (IS_ITEMP(IC_RESULT(ic)) && - (OP_SYMBOL(IC_RESULT(ic))->nRegs || OP_SYMBOL(IC_RESULT(ic))->spildir || - OP_SYMBOL(IC_RESULT(ic))->accuse == ACCUSE_A)) || - IS_TRUE_SYMOP(IC_RESULT(ic)); - /* adjust the stack for parameters if required */ if ((ic->parmBytes || bigreturn) && - IFFUNC_ISNORETURN(OP_SYMBOL(IC_LEFT(ic))->type)) { - /* This is just a workaround to not confuse the peephole optimizer too much. - */ - /* Todo: Check for _Noreturn in the peephole optimizer and do not emit the - * inc sp here. */ - emit2("inc sp"); - regalloc_dry_run_cost += 1; - if (!regalloc_dry_run) + (IFFUNC_ISNORETURN(ftype) || z88dk_callee)) { + if (!regalloc_dry_run) { _G.stack.pushed -= (ic->parmBytes + bigreturn * 2); - } else if (ic->parmBytes || bigreturn) { + z80_symmParm_in_calls_from_current_function = FALSE; + } + } else if ((ic->parmBytes || bigreturn)) { adjustStack(ic->parmBytes + bigreturn * 2, !IS_TLCS90, TRUE, - !SomethingReturned, !IY_RESERVED); + !SomethingReturned || bigreturn, !IY_RESERVED); if (regalloc_dry_run) _G.stack.pushed += ic->parmBytes + bigreturn * 2; @@ -3732,11 +4612,9 @@ static void emitCall(const iCode *ic, bool ispcall) { /* if we need assign a result value */ if (SomethingReturned && !bigreturn) { - aopOp(IC_RESULT(ic), ic, FALSE, FALSE); + genMove(IC_RESULT(ic)->aop, ASMOP_RETURN, true, true, true); - assignResultValue(IC_RESULT(ic)); - - freeAsmop(IC_RESULT(ic), NULL); + freeAsmop(IC_RESULT(ic), 0); } spillCached(); @@ -3749,16 +4627,6 @@ static void emitCall(const iCode *ic, bool ispcall) { _G.stack.pushedHL = FALSE; } -/*-----------------------------------------------------------------*/ -/* genCall - generates a call statement */ -/*-----------------------------------------------------------------*/ -static void genCall(const iCode *ic) { emitCall(ic, FALSE); } - -/*-----------------------------------------------------------------*/ -/* genPcall - generates a call by pointer statement */ -/*-----------------------------------------------------------------*/ -static void genPcall(const iCode *ic) { emitCall(ic, TRUE); } - /*-----------------------------------------------------------------*/ /* resultRemat - result is rematerializable */ /*-----------------------------------------------------------------*/ @@ -3795,6 +4663,7 @@ static void genFunction(const iCode *ic) { doesn't seem to get reset anywhere else. */ _G.receiveOffset = 0; + _G.stack.param_offset = sym->type->funcAttrs.z88dk_params_offset; /* Record the last function name for debugging. */ _G.lastFunctionName = sym->rname; @@ -3807,17 +4676,22 @@ static void genFunction(const iCode *ic) { : "; Register assignment might be sub-optimal."); emitDebug("; Stack space usage: %d bytes.", sym->stack); - if (!IS_STATIC(sym->etype)) { - struct dbuf_s dbuf; - - dbuf_init(&dbuf, 128); - dbuf_printf(&dbuf, "%s_start", sym->rname); - emit2("!labeldef", dbuf_c_str(&dbuf)); - dbuf_detach(&dbuf); - if (!regalloc_dry_run) - genLine.lineCurr->isLabel = 1; + if (IFFUNC_BANKED(sym->type)) { + int bank_number = 0; + for (int i = strlen(options.code_seg) - 1; i >= 0; i--) { + if (!isdigit(options.code_seg[i]) && options.code_seg[i + 1] != '\0') { + bank_number = atoi(&options.code_seg[i + 1]); + break; + } + } + emit2("!bequ", sym->rname, bank_number); } - emit2("!functionlabeldef", sym->rname); + + if (IS_STATIC(sym->etype)) + emit2("!functionlabeldef", sym->rname); + else + emit2("!globalfunctionlabeldef", sym->rname); + if (!regalloc_dry_run) genLine.lineCurr->isLabel = 1; @@ -3831,15 +4705,8 @@ static void genFunction(const iCode *ic) { /* if this is an interrupt service routine then save all potentially used registers. */ if (IFFUNC_ISISR(sym->type)) { - if (IS_RAB) - emit2("push ip"); - - /* If critical function then turn interrupts off */ - /* except when no interrupt number is given then it implies the NMI handler - */ - if (IFFUNC_ISCRITICAL(sym->type) && - (FUNC_INTNO(sym->type) != INTNO_UNSPEC)) { - emit2("!di"); + if (!IFFUNC_ISCRITICAL(sym->type)) { + emit2("!ei"); } emit2("!pusha"); @@ -3850,11 +4717,17 @@ static void genFunction(const iCode *ic) { if (IS_GB || IS_RAB || IS_TLCS90) { emit2("!di"); } else { - // get interrupt enable flag IFF2 into P/O - emit2("ld a,i"); - emit2("!di"); + if (z80_opts.nmosZ80) + emit2("call ___sdcc_critical_enter"); + else { + // get interrupt enable flag IFF2 into P/O + emit2("ld a,i"); + // disable interrupts + emit2("!di"); + } // save P/O flag emit2("push af"); + _G.stack.param_offset += 2; } } } @@ -3863,10 +4736,6 @@ static void genFunction(const iCode *ic) { emit2("!profileenter"); } - /* PENDING: callee-save etc */ - - _G.stack.param_offset = 0; - if (z80_opts.calleeSavesBC) { bcInUse = TRUE; } @@ -3930,13 +4799,32 @@ static void genFunction(const iCode *ic) { if (!regalloc_dry_run) _G.omitFramePtr = TRUE; } else if (sym->stack) { - if (!_G.omitFramePtr) - emit2("!enter"); - adjustStack(-sym->stack, !IS_TLCS90, TRUE, TRUE, !IY_RESERVED); + if (IS_EZ80_Z80 && !_G.omitFramePtr && -sym->stack > -128 && + -sym->stack <= -3 && IFFUNC_ISZ88DK_FASTCALL(ftype)) { + emit2("push ix"); + emit2("ld ix, !immed%d", -sym->stack); + emit2("add ix, sp"); + emit2("ld sp, ix"); + emit2("lea ix, ix, !immed%d", sym->stack); + regalloc_dry_run_cost += 3; + } else { + if (!_G.omitFramePtr) + emit2((optimize.codeSize && !IFFUNC_ISZ88DK_FASTCALL(ftype)) + ? "!enters" + : "!enter"); + if (IS_EZ80_Z80 && !_G.omitFramePtr && -sym->stack > -128 && + -sym->stack <= -3 && !IFFUNC_ISZ88DK_FASTCALL(ftype)) { + emit2("lea hl, ix, !immed%d", -sym->stack); + emit2("ld sp, hl"); + regalloc_dry_run_cost += 4; + } else + adjustStack(-sym->stack, !IS_TLCS90, TRUE, + !IFFUNC_ISZ88DK_FASTCALL(ftype), !IY_RESERVED); + } _G.stack.pushed = 0; } else if (!_G.omitFramePtr) { - if (!_G.omitFramePtr) - emit2("!enter"); + emit2((optimize.codeSize && !IFFUNC_ISZ88DK_FASTCALL(ftype)) ? "!enters" + : "!enter"); } _G.stack.offset = sym->stack; @@ -3948,44 +4836,45 @@ static void genFunction(const iCode *ic) { static void genEndFunction(iCode *ic) { symbol *sym = OP_SYMBOL(IC_LEFT(ic)); int retsize = getSize(sym->type->next); + /* __critical __interrupt without an interrupt number is the non-maskable + * interrupt */ + bool is_nmi = (IS_Z80 || IS_Z180 || IS_EZ80_Z80 || IS_Z80N) && + IFFUNC_ISCRITICAL(sym->type) && + FUNC_INTNO(sym->type) == INTNO_UNSPEC; wassert(!regalloc_dry_run); - wassert(!_G.stack.pushed); + wassertl(!_G.stack.pushed, "Unbalanced stack."); if (IFFUNC_ISNAKED(sym->type) || IFFUNC_ISNORETURN(sym->type)) { emitDebug(IFFUNC_ISNAKED(sym->type) ? "; naked function: No epilogue." : "; _Noreturn function: No epilogue."); - if (!IS_STATIC(sym->etype)) { - struct dbuf_s dbuf; - - dbuf_init(&dbuf, 128); - dbuf_printf(&dbuf, "%s_end", sym->rname); - emit2("!labeldef", dbuf_c_str(&dbuf)); - dbuf_destroy(&dbuf); - emit2(".function %s, %s_start, %s_end", sym->rname, sym->rname, - sym->rname); - genLine.lineCurr->isLabel = 1; - } return; } + wassertl(regalloc_dry_run || !IFFUNC_ISZ88DK_CALLEE(sym->type), + "Unimplemented __z88dk_callee support on callee side"); + if (!IS_GB && !_G.omitFramePtr && sym->stack > (optimize.codeSize ? 2 : 1)) { emit2("ld sp, ix"); - cost2(2, 10, 7, 4, 0, 6); + cost2(2, 10, 7, 4, 0, 6, 2); } else adjustStack(_G.stack.offset, !IS_TLCS90, TRUE, retsize == 0 || retsize > 4, !IY_RESERVED); - if (!IS_GB && !_G.omitFramePtr) + if (!IS_GB && !_G.omitFramePtr) { emit2("pop ix"); + regalloc_dry_run_cost += 2; + } if (_G.calleeSaves.pushedDE) { emit2("pop de"); + regalloc_dry_run_cost++; _G.calleeSaves.pushedDE = FALSE; } if (_G.calleeSaves.pushedBC) { emit2("pop bc"); + regalloc_dry_run_cost++; _G.calleeSaves.pushedBC = FALSE; } @@ -3997,14 +4886,7 @@ static void genEndFunction(iCode *ic) { then save all potentially used registers. */ if (IFFUNC_ISISR(sym->type)) { emit2("!popa"); - - /* If critical function then turn interrupts back on */ - /* except when no interrupt number is given then it implies the NMI handler - */ - if (IFFUNC_ISCRITICAL(sym->type) && - (FUNC_INTNO(sym->type) != INTNO_UNSPEC)) { - emit2("!ei"); - } + regalloc_dry_run_cost++; } else { /* This is a non-ISR function. If critical function then turn interrupts back on */ @@ -4028,32 +4910,23 @@ static void genEndFunction(iCode *ic) { } if (IFFUNC_ISISR(sym->type)) { - /* "critical interrupt" is used to imply NMI handler */ - if (!IS_GB && IFFUNC_ISCRITICAL(sym->type) && - FUNC_INTNO(sym->type) == INTNO_UNSPEC) + if (is_nmi) emit2("retn"); else if (IS_RAB && IFFUNC_ISCRITICAL(sym->type) && FUNC_INTNO(sym->type) == INTNO_UNSPEC) { - // ISR exit sequence that works on the rabbit 4000 - emit2("pop ip"); emit2("ipres"); emit2("ret"); - } else + } else if (IS_GB) + emit2(IFFUNC_ISCRITICAL(sym->type) ? "reti" : "ret"); + else { + if (IFFUNC_ISCRITICAL(sym->type) && !is_nmi) + emit2("!ei"); emit2("reti"); + } } else { /* Both banked and non-banked just ret */ emit2("ret"); - } - - if (!IS_STATIC(sym->etype)) { - struct dbuf_s dbuf; - - dbuf_init(&dbuf, 128); - dbuf_printf(&dbuf, "%s_end", sym->rname); - emit2("!labeldef", dbuf_c_str(&dbuf)); - dbuf_destroy(&dbuf); - emit2(".function %s, %s_start, %s_end", sym->rname, sym->rname, sym->rname); - genLine.lineCurr->isLabel = 1; + regalloc_dry_run_cost++; } _G.flushStatics = 1; @@ -4082,38 +4955,52 @@ static void genRet(const iCode *ic) { if (size == 2) { fetchPairLong(IS_GB ? PAIR_DE : PAIR_HL, AOP(IC_LEFT(ic)), ic, 0); } else if (size <= 4) { - if (IS_GB && size == 4 && requiresHL(AOP(IC_LEFT(ic)))) { + if (IC_LEFT(ic)->aop->type == AOP_REG) + genMove_o(ASMOP_RETURN, 0, IC_LEFT(ic)->aop, 0, IC_LEFT(ic)->aop->size, + true, true, true); + else if (IS_GB && size == 4 && requiresHL(AOP(IC_LEFT(ic)))) { fetchPairLong(PAIR_DE, AOP(IC_LEFT(ic)), 0, 0); fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, 2); - } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG) { - int i; - short retarray[4], oparray[4]; - - for (i = 0; i < AOP_SIZE(IC_LEFT(ic)); i++) { - retarray[i] = _fReturn3[i]->aopu.aop_reg[0]->rIdx; - oparray[i] = AOP(IC_LEFT(ic))->aopu.aop_reg[i]->rIdx; - } - - regMove(retarray, oparray, AOP_SIZE(IC_LEFT(ic)), FALSE); + } else if (size == 4 && + (IC_LEFT(ic)->aop->type == AOP_HL || + IC_LEFT(ic)->aop->type == AOP_IY)) // Use ld rr, (nn) + { + fetchPairLong(PAIR_DE, IC_LEFT(ic)->aop, 0, 2); + fetchPairLong(PAIR_HL, IC_LEFT(ic)->aop, 0, 0); } else { - while (size--) { - cheapMove(_fReturn3[offset], 0, AOP(IC_LEFT(ic)), offset); - offset++; + bool skipbytes[4] = {false, false, false, + false}; // Take care to not overwrite hl. + for (offset = 0; offset < size; offset++) { + if (requiresHL(AOP(IC_LEFT(ic))) && + (ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == H_IDX || + ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == L_IDX)) { + skipbytes[offset] = true; + continue; + } + cheapMove(ASMOP_RETURN, offset, IC_LEFT(ic)->aop, offset, true); } + for (offset = 0; offset < size; offset++) + if (skipbytes[offset] && offset + 1 < size && + ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == L_IDX && + ASMOP_RETURN->aopu.aop_reg[offset + 1]->rIdx == H_IDX) { + fetchPairLong(PAIR_HL, IC_LEFT(ic)->aop, 0, offset); + break; + } else if (skipbytes[offset]) { + cheapMove(ASMOP_RETURN, offset, IC_LEFT(ic)->aop, offset, true); + } } } else if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) { - unsigned long lit = ulFromVal(AOP(IC_LEFT(ic))->aopu.aop_lit); - emit2("ld hl, #%d", _G.stack.offset + _G.stack.param_offset + - _G.stack.pushed + - (_G.omitFramePtr || IS_GB ? 0 : 2)); - emit2("add hl, sp"); + unsigned long long lit = ullFromVal(AOP(IC_LEFT(ic))->aopu.aop_lit); + setupPairFromSP(PAIR_HL, _G.stack.offset + _G.stack.param_offset + + _G.stack.pushed + + (_G.omitFramePtr || IS_GB ? 0 : 2)); emit2("ld a, (hl)"); emit2("inc hl"); emit2("ld h, (hl)"); emit2("ld l, a"); regalloc_dry_run_cost += 8; do { - emit2("ld (hl), !immedbyte", lit & 0xff); + emit2("ld (hl), !immedbyte", (unsigned long)(lit & 0xff)); regalloc_dry_run_cost += 2; lit >>= 8; if (size > 1) { @@ -4121,15 +5008,18 @@ static void genRet(const iCode *ic) { regalloc_dry_run_cost++; } } while (--size); - } else if (!IS_GB && AOP_TYPE(IC_LEFT(ic)) == AOP_STK || - AOP_TYPE(IC_LEFT(ic)) == AOP_EXSTK || - AOP_TYPE(IC_LEFT(ic)) == AOP_DIR || - AOP_TYPE(IC_LEFT(ic)) == AOP_IY) { - emit2("ld hl, #%d", _G.stack.offset + _G.stack.param_offset + - _G.stack.pushed + - (_G.omitFramePtr || IS_GB ? 0 : 2)); - emit2("add hl, sp"); - emit2("ld e, (hl)"); + } + // gbz80 doesn't have have ldir. r2k and r2ka have an ldir wait state bug that + // affects copies between different types of memory. + else if (!IS_GB && AOP_TYPE(IC_LEFT(ic)) == AOP_STK || + AOP_TYPE(IC_LEFT(ic)) == AOP_EXSTK || + (AOP_TYPE(IC_LEFT(ic)) == AOP_DIR || + AOP_TYPE(IC_LEFT(ic)) == AOP_IY) && + !(IS_R2K || IS_R2KA)) { + setupPairFromSP(PAIR_HL, _G.stack.offset + _G.stack.param_offset + + _G.stack.pushed + + (_G.omitFramePtr || IS_GB ? 0 : 2)); + emit2("ld e, l)"); emit2("inc hl"); emit2("ld d, (hl)"); regalloc_dry_run_cost += 7; @@ -4137,31 +5027,30 @@ static void genRet(const iCode *ic) { AOP_TYPE(IC_LEFT(ic)) == AOP_EXSTK) { int sp_offset, fp_offset; fp_offset = - AOP(IC_LEFT(ic))->aopu.aop_stk + _G.stack.offset + + AOP(IC_LEFT(ic))->aopu.aop_stk + (AOP(IC_LEFT(ic))->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - sp_offset = fp_offset + _G.stack.pushed; - emit2("ld hl, #%d", sp_offset); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2("ld hl, !immed%d", sp_offset); emit2("add hl, sp"); regalloc_dry_run_cost += 4; } else { - emit2("ld hl, #%s", AOP(IC_LEFT(ic))->aopu.aop_dir); + emit2("ld hl, !hashedstr", AOP(IC_LEFT(ic))->aopu.aop_dir); regalloc_dry_run_cost += 3; } - emit2("ld bc, #%d", size); + emit2("ld bc, !immed%d", size); emit2("ldir"); regalloc_dry_run_cost += 5; } else { - emit2("ld hl, #%d", _G.stack.offset + _G.stack.param_offset + - _G.stack.pushed + - (_G.omitFramePtr || IS_GB ? 0 : 2)); - emit2("add hl, sp"); + setupPairFromSP(PAIR_HL, _G.stack.offset + _G.stack.param_offset + + _G.stack.pushed + + (_G.omitFramePtr || IS_GB ? 0 : 2)); emit2("ld c, (hl)"); emit2("inc hl"); emit2("ld b, (hl)"); regalloc_dry_run_cost += 7; spillPair(PAIR_HL); do { - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset++); + cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset++, true); emit2("ld (bc), a"); regalloc_dry_run_cost++; if (size > 1) { @@ -4224,18 +5113,36 @@ static bool genPlusIncr(const iCode *ic) { fetchLitPair(getPairId(AOP(IC_RESULT(ic))), AOP(IC_LEFT(ic)), icount); return TRUE; } + + if (IS_Z80N && resultId == getPairId(IC_LEFT(ic)->aop) && + resultId != PAIR_IY && icount > 3 && icount < 256 && + !bitVectBitValue(ic->rSurv, + A_IDX)) // Saves once cycle vs. add dd, nn below. + { + cheapMove(ASMOP_A, 0, IC_RIGHT(ic)->aop, 0, true); + emit2("add %s, a", getPairName(IC_RESULT(ic)->aop)); + regalloc_dry_run_cost += 2; + return true; + } else if (IS_Z80N && resultId == getPairId(IC_LEFT(ic)->aop) && + resultId != PAIR_IY && icount > 3) { + emit2("add %s, #%s", getPairName(IC_RESULT(ic)->aop), + aopGetLitWordLong(IC_RIGHT(ic)->aop, 0, false)); + regalloc_dry_run_cost += 4; + return true; + } + if (isPair(AOP(IC_LEFT(ic))) && resultId == PAIR_HL && icount > 3) { - if (getPairId(AOP(IC_LEFT(ic))) == PAIR_HL) { + if (getPairId(IC_LEFT(ic)->aop) == PAIR_HL) { PAIR_ID freep = getDeadPairId(ic); if (freep != PAIR_INVALID) { fetchPair(freep, AOP(IC_RIGHT(ic))); - emit2("add hl,%s", _pairs[freep].name); + emit2("add hl, %s", _pairs[freep].name); regalloc_dry_run_cost += 1; return TRUE; } } else { - fetchPair(resultId, AOP(IC_RIGHT(ic))); - emit2("add hl,%s", getPairName(AOP(IC_LEFT(ic)))); + fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); + emit2("add hl, %s", getPairName(AOP(IC_LEFT(ic)))); regalloc_dry_run_cost += 1; return TRUE; } @@ -4250,7 +5157,7 @@ static bool genPlusIncr(const iCode *ic) { if (icount > 3) return FALSE; if (!delayed_move) - fetchPair(getPairId(AOP(IC_RESULT(ic))), AOP(IC_LEFT(ic))); + fetchPairLong(getPairId(IC_RESULT(ic)->aop), IC_LEFT(ic)->aop, ic, 0); } while (icount--) { PAIR_ID pair = delayed_move ? getPairId(AOP(IC_LEFT(ic))) @@ -4266,22 +5173,39 @@ static bool genPlusIncr(const iCode *ic) { if (!IS_GB && isLitWord(AOP(IC_LEFT(ic))) && size == 2 && isPairDead(PAIR_HL, ic)) { fetchLitPair(PAIR_HL, AOP(IC_LEFT(ic)), icount); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - return TRUE; + genMove(IC_RESULT(ic)->aop, ASMOP_HL, !bitVectBitValue(ic->rSurv, A_IDX), + true, isPairDead(PAIR_DE, ic)); + return true; } - /* if the literal value of the right hand side - is greater than 4 then it is not worth it */ - if (icount > 4) - return FALSE; + if (icount > 4) // Not worth it if the sequence of inc gets too long. + return false; + + if (icount > 1 && size == 1 && + aopInReg(IC_LEFT(ic)->aop, 0, + A_IDX)) // add a, #n is cheaper than sequence of inc a. + return false; if (size == 2 && getPairId(AOP(IC_LEFT(ic))) != PAIR_INVALID && icount <= 3 && isPairDead(getPairId(AOP(IC_LEFT(ic))), ic)) { PAIR_ID pair = getPairId(AOP(IC_LEFT(ic))); while (icount--) emit2("inc %s", _pairs[pair].name); - commitPair(AOP(IC_RESULT(ic)), pair, ic, FALSE); - return TRUE; + genMove(IC_RESULT(ic)->aop, IC_LEFT(ic)->aop, + !bitVectBitValue(ic->rSurv, A_IDX), isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); + return true; + } + + if (size == 2 && icount <= 2 && isPairDead(PAIR_HL, ic) && !IS_GB && + (IC_LEFT(ic)->aop->type == AOP_HL || IC_LEFT(ic)->aop->type == AOP_IY)) { + fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); + while (icount--) + emit2("inc hl"); + regalloc_dry_run_cost++; + genMove(IC_RESULT(ic)->aop, ASMOP_HL, !bitVectBitValue(ic->rSurv, A_IDX), + true, isPairDead(PAIR_DE, ic)); + return true; } /* if increment 16 bits in register */ @@ -4290,10 +5214,17 @@ static bool genPlusIncr(const iCode *ic) { int offset = 0; symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel(0); while (size--) { + if (size == 1 && + getPairId_o(AOP(IC_RESULT(ic)), offset) != PAIR_INVALID) { + emit2("inc %s", _pairs[getPairId_o(AOP(IC_RESULT(ic)), offset)].name); + size--; + offset += 2; + break; + } emit3_o(A_INC, AOP(IC_RESULT(ic)), offset++, 0, 0); if (size) { if (!regalloc_dry_run) - emit2("jp NZ,!tlabel", labelKey2num(tlbl->key)); + emit2("jp NZ, !tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 3; } } @@ -4314,7 +5245,7 @@ static bool genPlusIncr(const iCode *ic) { /* If the result is in a register then we can load then increment. */ if (AOP_TYPE(IC_RESULT(ic)) == AOP_REG) { - cheapMove(AOP(IC_RESULT(ic)), LSB, AOP(IC_LEFT(ic)), LSB); + cheapMove(AOP(IC_RESULT(ic)), LSB, AOP(IC_LEFT(ic)), LSB, true); while (icount--) emit3_o(A_INC, AOP(IC_RESULT(ic)), LSB, 0, 0); return TRUE; @@ -4342,8 +5273,8 @@ static void outBitAcc(operand *result) { wassertl(0, "Tried to write A into a bit"); } else { if (!regalloc_dry_run) { - emit2("jp Z,!tlabel", labelKey2num(tlbl->key)); - emit2("ld a,!one"); + emit2("jp Z, !tlabel", labelKey2num(tlbl->key)); + emit2("ld a, !one"); emitLabel(tlbl); } regalloc_dry_run_cost += 5; @@ -4351,7 +5282,7 @@ static void outBitAcc(operand *result) { } } -static bool couldDestroyCarry(asmop *aop) { +static bool couldDestroyCarry(const asmop *aop) { if (aop) { if (aop->type == AOP_EXSTK || aop->type == AOP_IY) { return TRUE; @@ -4360,27 +5291,22 @@ static bool couldDestroyCarry(asmop *aop) { return FALSE; } -static void shiftIntoPair(int idx, asmop *aop) { - PAIR_ID id = PAIR_INVALID; - +static void shiftIntoPair(PAIR_ID id, asmop *aop) { wassertl(!IS_GB, "Not implemented for the GBZ80"); - emitDebug("; Shift into pair idx %u", idx); + emitDebug("; Shift into pair"); - switch (idx) { - case 0: - id = PAIR_HL; + switch (id) { + case PAIR_HL: setupPair(PAIR_HL, aop, 0); break; - case 1: - id = PAIR_DE; + case PAIR_DE: _push(PAIR_DE); setupPair(PAIR_IY, aop, 0); emit2("push iy"); emit2("pop %s", _pairs[id].name); break; - case 2: - id = PAIR_IY; + case PAIR_IY: setupPair(PAIR_IY, aop, 0); break; default: @@ -4393,32 +5319,26 @@ static void shiftIntoPair(int idx, asmop *aop) { _G.pairs[id].last_type = aop->type; } -static void setupToPreserveCarry(const iCode *ic) { - asmop *left = AOP(IC_LEFT(ic)); - asmop *right = AOP(IC_RIGHT(ic)); - asmop *result = AOP(IC_RESULT(ic)); - +static void setupToPreserveCarry(asmop *result, asmop *left, asmop *right) { wassert(left && right); if (!IS_GB) { if (couldDestroyCarry(right) && couldDestroyCarry(result)) { - shiftIntoPair(0, right); + shiftIntoPair(PAIR_HL, right); /* check result again, in case right == result */ if (couldDestroyCarry(result)) { if (couldDestroyCarry(left)) - shiftIntoPair(1, result); + shiftIntoPair(PAIR_DE, result); else - shiftIntoPair(2, result); + shiftIntoPair(PAIR_IY, result); } } else if (couldDestroyCarry(right)) { if (getPairId(result) == PAIR_HL) _G.preserveCarry = TRUE; else - shiftIntoPair(0, right); + shiftIntoPair(PAIR_HL, right); } else if (couldDestroyCarry(result)) { - shiftIntoPair(0, result); - } else { - /* Fine */ + shiftIntoPair(PAIR_HL, result); } } } @@ -4427,9 +5347,12 @@ static void setupToPreserveCarry(const iCode *ic) { /* genPlus - generates code for addition */ /*-----------------------------------------------------------------*/ static void genPlus(iCode *ic) { - int size, offset = 0; + int size, i, offset = 0; signed char cached[2]; - bool premoved; + bool premoved, started; + asmop *leftop; + asmop *rightop; + symbol *tlbl = 0; /* special cases :- */ @@ -4443,12 +5366,15 @@ static void genPlus(iCode *ic) { if left requires ACC or right is already in ACC */ if ((AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) || (AOP_NEEDSACC(IC_RIGHT(ic))) || - AOP_TYPE(IC_RIGHT(ic)) == AOP_ACC) { + aopInReg(IC_RIGHT(ic)->aop, 0, A_IDX)) { operand *t = IC_RIGHT(ic); IC_RIGHT(ic) = IC_LEFT(ic); IC_LEFT(ic) = t; } + leftop = IC_LEFT(ic)->aop; + rightop = IC_RIGHT(ic)->aop; + /* if both left & right are in bit space */ if (AOP_TYPE(IC_LEFT(ic)) == AOP_CRY && AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { @@ -4483,7 +5409,7 @@ static void genPlus(iCode *ic) { dbuf_init(&dbuf, 128); dbuf_printf(&dbuf, "#(%s + %s)", left, right); Safe_free(left); - emit2("ld %s,%s", getPairName(AOP(IC_RESULT(ic))), dbuf_c_str(&dbuf)); + emit2("ld %s, %s", getPairName(AOP(IC_RESULT(ic))), dbuf_c_str(&dbuf)); dbuf_destroy(&dbuf); regalloc_dry_run_cost += (getPairId(AOP(IC_RESULT(ic))) == PAIR_IY ? 4 : 3); @@ -4492,6 +5418,20 @@ static void genPlus(iCode *ic) { Safe_free(left); } + // eZ80 has lea. + if (IS_EZ80_Z80 && isPair(IC_RESULT(ic)->aop) && + getPairId(IC_LEFT(ic)->aop) == PAIR_IY && + IC_RIGHT(ic)->aop->type == AOP_LIT) { + int lit = (int)ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit); + if (lit >= -128 && lit < 128) { + emit2("lea %s, iy, !immed%d", _pairs[getPairId(IC_RESULT(ic)->aop)].name, + lit); + regalloc_dry_run_cost += 3; + spillPair(getPairId(IC_RESULT(ic)->aop)); + goto release; + } + } + if ((isPair(AOP(IC_RIGHT(ic))) || isPair(AOP(IC_LEFT(ic)))) && getPairId(AOP(IC_RESULT(ic))) == PAIR_HL) { /* Fetch into HL then do the add */ @@ -4501,293 +5441,126 @@ static void genPlus(iCode *ic) { spillPair(PAIR_HL); if (left == PAIR_HL && right != PAIR_INVALID) { - emit2("add hl,%s", _pairs[right].name); + emit2("add hl, %s", _pairs[right].name); regalloc_dry_run_cost += 1; goto release; } else if (right == PAIR_HL && left != PAIR_INVALID) { - emit2("add hl,%s", _pairs[left].name); + emit2("add hl, %s", _pairs[left].name); regalloc_dry_run_cost += 1; goto release; } else if (right != PAIR_INVALID && right != PAIR_HL) { fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - emit2("add hl,%s", getPairName(AOP(IC_RIGHT(ic)))); + emit2("add hl, %s", getPairName(AOP(IC_RIGHT(ic)))); regalloc_dry_run_cost += 1; goto release; } else if (left != PAIR_INVALID && left != PAIR_HL) { fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - emit2("add hl,%s", getPairName(AOP(IC_LEFT(ic)))); + emit2("add hl, %s", getPairName(AOP(IC_LEFT(ic)))); regalloc_dry_run_cost += 1; goto release; } else if (left == PAIR_HL && (isPairDead(PAIR_DE, ic) || isPairDead(PAIR_BC, ic))) { PAIR_ID pair = (isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC); fetchPair(pair, AOP(IC_RIGHT(ic))); - emit2("add hl,%s", _pairs[pair].name); + emit2("add hl, %s", _pairs[pair].name); regalloc_dry_run_cost += 1; goto release; } else if (right == PAIR_HL && (isPairDead(PAIR_DE, ic) || isPairDead(PAIR_BC, ic))) { PAIR_ID pair = (isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC); fetchPair(pair, AOP(IC_LEFT(ic))); - emit2("add hl,%s", _pairs[pair].name); - regalloc_dry_run_cost += 1; - goto release; - } else { - /* Can't do it */ - } - } - - /* Addition of interleaved pairs */ - if (getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP_SIZE(IC_LEFT(ic)) >= 2 && - AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP_SIZE(IC_RIGHT(ic)) >= 2) { - if (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == L_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == H_IDX) { - if (AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == D_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX) { - emit2("add hl, de"); - regalloc_dry_run_cost += 1; - goto release; - } else if (AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == B_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX) { - emit2("add hl, bc"); - regalloc_dry_run_cost += 1; - goto release; - } - } else if (AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == H_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == L_IDX) { - if (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == D_IDX) { - emit2("add hl, de"); - regalloc_dry_run_cost += 1; - goto release; - } else if (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && - AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == B_IDX) { - emit2("add hl, bc"); - regalloc_dry_run_cost += 1; - goto release; - } - } - } - - if (getPairId(AOP(IC_RESULT(ic))) == PAIR_IY && - (getPairId(AOP(IC_LEFT(ic))) == PAIR_HL && isPair(AOP(IC_RIGHT(ic))) && - getPairId(AOP(IC_RIGHT(ic))) != PAIR_IY || - getPairId(AOP(IC_RIGHT(ic))) == PAIR_HL && isPair(AOP(IC_LEFT(ic))) && - getPairId(AOP(IC_LEFT(ic))) != PAIR_IY) && - isPairDead(PAIR_HL, ic)) { - PAIR_ID pair = - (getPairId(AOP(IC_LEFT(ic))) == PAIR_HL ? getPairId(AOP(IC_RIGHT(ic))) - : getPairId(AOP(IC_LEFT(ic)))); - emit2("add hl, %s", _pairs[pair].name); - _push(PAIR_HL); - _pop(PAIR_IY); - goto release; - } else if (getPairId(AOP(IC_RESULT(ic))) == PAIR_IY) { - bool save_pair = FALSE; - PAIR_ID pair; - - if (getPairId(AOP(IC_RIGHT(ic))) == PAIR_IY || - getPairId(AOP(IC_LEFT(ic))) == PAIR_BC || - getPairId(AOP(IC_LEFT(ic))) == PAIR_DE || - getPairId(AOP(IC_LEFT(ic))) != PAIR_IY && - (AOP_TYPE(IC_RIGHT(ic)) == AOP_IMMD || - AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT || - AOP_TYPE(IC_RIGHT(ic)) == AOP_SIMPLELIT)) { - operand *t = IC_RIGHT(ic); - IC_RIGHT(ic) = IC_LEFT(ic); - IC_LEFT(ic) = t; - } - pair = getPairId(AOP(IC_RIGHT(ic))); - if (pair != PAIR_BC && pair != PAIR_DE) { - if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && - (!bitVectBitValue(ic->rSurv, B_IDX) || !isPairDead(PAIR_DE, ic))) - pair = PAIR_BC; - else if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && - (!bitVectBitValue(ic->rSurv, D_IDX) || !isPairDead(PAIR_BC, ic))) - pair = PAIR_DE; - else - pair = isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC; - if (!isPairDead(pair, ic)) - save_pair = TRUE; - } - fetchPair(PAIR_IY, AOP(IC_LEFT(ic))); - if (save_pair) - _push(pair); - fetchPair(pair, AOP(IC_RIGHT(ic))); - emit2("add iy, %s", _pairs[pair].name); - regalloc_dry_run_cost += 2; - if (save_pair) - _pop(pair); - goto release; - } - - if (isPair(AOP(IC_RIGHT(ic))) && AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD && - getPairId(AOP(IC_RIGHT(ic))) != PAIR_HL && isPairDead(PAIR_HL, ic)) { - fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - emit2("add hl,%s", getPairName(AOP(IC_RIGHT(ic)))); - regalloc_dry_run_cost += 1; - spillPair(PAIR_HL); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - goto release; - } - - if (isPair(AOP(IC_LEFT(ic))) && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT && - getPairId(AOP(IC_LEFT(ic))) != PAIR_HL && isPairDead(PAIR_HL, ic)) { - fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - emit2("add hl,%s", getPairName(AOP(IC_LEFT(ic)))); - regalloc_dry_run_cost += 1; - spillPair(PAIR_HL); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - goto release; - } - - if (isPair(AOP(IC_LEFT(ic))) && isPair(AOP(IC_RIGHT(ic))) && - getPairId(AOP(IC_LEFT(ic))) == PAIR_HL && isPairDead(PAIR_HL, ic)) { - emit2("add hl,%s", getPairName(AOP(IC_RIGHT(ic)))); - regalloc_dry_run_cost += 1; - spillPair(PAIR_HL); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - goto release; - } - - if (isPair(AOP(IC_LEFT(ic))) && isPair(AOP(IC_RIGHT(ic))) && - getPairId(AOP(IC_RIGHT(ic))) == PAIR_HL && isPairDead(PAIR_HL, ic)) { - emit2("add hl,%s", getPairName(AOP(IC_LEFT(ic)))); - regalloc_dry_run_cost += 1; - spillPair(PAIR_HL); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - goto release; - } - - if (getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && - !bitVectBitValue(ic->rSurv, B_IDX)) { - if (AOP(IC_RIGHT(ic))->aopu.aop_reg[1] && - (AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == H_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == L_IDX)) { - cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), 1); - fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - } else { - fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), 1); - } - emit2("add hl, bc"); - regalloc_dry_run_cost += 1; - goto release; - } - if (!options.oldralloc && getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && - !bitVectBitValue(ic->rSurv, B_IDX)) { - if (AOP(IC_LEFT(ic))->aopu.aop_reg[1] && - (AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == H_IDX || - AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == L_IDX)) { - cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), 1); - fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); + emit2("add hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + goto release; } else { - fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), 1); + /* Can't do it */ } - emit2("add hl, bc"); + } else if (getPairId(IC_RESULT(ic)->aop) == PAIR_HL && + (isPairDead(PAIR_DE, ic) || isPairDead(PAIR_BC, ic)) && + IC_RIGHT(ic)->aop->type == AOP_LIT) { + PAIR_ID extrapair = isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + fetchPair(PAIR_HL, IC_LEFT(ic)->aop); + fetchPair(extrapair, IC_RIGHT(ic)->aop); + emit2("add hl, %s", _pairs[extrapair].name); regalloc_dry_run_cost += 1; goto release; } - if (!options.oldralloc && getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && - !bitVectBitValue(ic->rSurv, D_IDX)) { - if (AOP(IC_RIGHT(ic))->aopu.aop_reg[1] && - (AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == H_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == L_IDX)) { - cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), 1); - fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - } else { - fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); - cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), 1); - } - emit2("add hl, de"); + // Handle AOP_EXSTK conflict with hl here, since setupToPreserveCarry() would + // cause problems otherwise. + if (IC_RESULT(ic)->aop->type == AOP_EXSTK && + (getPairId(IC_LEFT(ic)->aop) == PAIR_HL || + getPairId(IC_RIGHT(ic)->aop) == PAIR_HL) && + (isPairDead(PAIR_DE, ic) || isPairDead(PAIR_BC, ic)) && + isPairDead(PAIR_HL, ic)) { + PAIR_ID extrapair = isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + fetchPair(extrapair, getPairId(IC_LEFT(ic)->aop) == PAIR_HL + ? IC_RIGHT(ic)->aop + : IC_LEFT(ic)->aop); + emit2("add hl, %s", _pairs[extrapair].name); regalloc_dry_run_cost += 1; + spillPair(PAIR_HL); + genMove(IC_RESULT(ic)->aop, ASMOP_HL, !bitVectBitValue(ic->rSurv, A_IDX), + true, isPairDead(PAIR_DE, ic)); goto release; - } - if (!options.oldralloc && getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && - !bitVectBitValue(ic->rSurv, D_IDX)) { - if (AOP(IC_LEFT(ic))->aopu.aop_reg[1] && - (AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == H_IDX || - AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx == L_IDX)) { - cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), 1); - fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - } else { - fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), 1); - } - emit2("add hl, de"); - regalloc_dry_run_cost += 1; + } else if (getPairId(AOP(IC_RESULT(ic))) == PAIR_IY && + (getPairId(AOP(IC_LEFT(ic))) == PAIR_HL && + isPair(AOP(IC_RIGHT(ic))) && + getPairId(AOP(IC_RIGHT(ic))) != PAIR_IY || + getPairId(AOP(IC_RIGHT(ic))) == PAIR_HL && + isPair(AOP(IC_LEFT(ic))) && + getPairId(AOP(IC_LEFT(ic))) != PAIR_IY) && + isPairDead(PAIR_HL, ic)) { + PAIR_ID pair = + (getPairId(AOP(IC_LEFT(ic))) == PAIR_HL ? getPairId(AOP(IC_RIGHT(ic))) + : getPairId(AOP(IC_LEFT(ic)))); + emit2("add hl, %s", _pairs[pair].name); + regalloc_dry_run_cost++; + _push(PAIR_HL); + _pop(PAIR_IY); goto release; - } + } else if (getPairId(IC_RESULT(ic)->aop) == PAIR_IY) { + bool save_pair = FALSE; + PAIR_ID pair; - if (getPairId(AOP(IC_RESULT(ic))) == PAIR_HL && - getPairId(AOP(IC_LEFT(ic))) == PAIR_HL && - AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT) { - PAIR_ID pair = getFreePairId(ic); - bool pair_alive; - if (pair == PAIR_INVALID) - pair = PAIR_DE; - if (pair_alive = !isPairDead(pair, ic)) + if (getPairId(AOP(IC_RIGHT(ic))) == PAIR_IY || + getPairId(AOP(IC_LEFT(ic))) == PAIR_BC || + getPairId(AOP(IC_LEFT(ic))) == PAIR_DE || + getPairId(AOP(IC_LEFT(ic))) != PAIR_IY && + (AOP_TYPE(IC_RIGHT(ic)) == AOP_IMMD || + AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT)) { + operand *t = IC_RIGHT(ic); + IC_RIGHT(ic) = IC_LEFT(ic); + IC_LEFT(ic) = t; + } + pair = getPairId(AOP(IC_RIGHT(ic))); + if (pair != PAIR_BC && pair != PAIR_DE) { + if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && + AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX && + (!bitVectBitValue(ic->rSurv, B_IDX) || !isPairDead(PAIR_DE, ic))) + pair = PAIR_BC; + else if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && + AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX && + (!bitVectBitValue(ic->rSurv, D_IDX) || !isPairDead(PAIR_BC, ic))) + pair = PAIR_DE; + else + pair = isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + if (!isPairDead(pair, ic)) + save_pair = TRUE; + } + fetchPair(PAIR_IY, AOP(IC_LEFT(ic))); + if (save_pair) _push(pair); fetchPair(pair, AOP(IC_RIGHT(ic))); - emit2("add hl, %s", _pairs[pair].name); - regalloc_dry_run_cost += 1; - if (pair_alive) + emit2("add iy, %s", _pairs[pair].name); + spillPair(PAIR_IY); + regalloc_dry_run_cost += 2; + if (save_pair) _pop(pair); goto release; } - /* Using the 16 bit addition results in smaller and faster code than using the - * 8-bit addition. */ - if (size == 1 && isPairDead(PAIR_HL, ic) && - AOP_TYPE(IC_RESULT(ic)) == AOP_REG && AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP(IC_RESULT(ic))->aopu.aop_reg[0]->rIdx == L_IDX && - (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == L_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == L_IDX) && - (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX)) { - PAIR_ID pair = (AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX || - AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX) - ? PAIR_BC - : PAIR_DE; - emit2("add hl, %s", _pairs[pair].name); - regalloc_dry_run_cost += 1; - goto release; - } - - /* When adding a literal, the 16 bit addition results in smaller, slower code. - */ - if (optimize.codeSize && size == 1 && isPairDead(PAIR_HL, ic) && - AOP_TYPE(IC_RESULT(ic)) == AOP_REG && AOP_TYPE(IC_LEFT(ic)) == AOP_REG && - AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT && - AOP(IC_RESULT(ic))->aopu.aop_reg[0]->rIdx == L_IDX && - AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == L_IDX && - (!bitVectBitValue(ic->rSurv, C_IDX) || - !bitVectBitValue(ic->rSurv, E_IDX))) { - PAIR_ID pair = bitVectBitValue(ic->rSurv, C_IDX) ? PAIR_DE : PAIR_BC; - emit2("ld %s, !immedbyte", _pairs[pair].l, - ((unsigned int)ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit)) & 0xff); - emit2("add hl, %s", _pairs[pair].name); - regalloc_dry_run_cost += 3; - goto release; - } - - /* Special case: + /* gbz80 special case: ld hl,sp+n trashes C so we can't afford to do it during an add with stack based variables. Worst case is: ld hl,sp+left @@ -4821,8 +5594,9 @@ static void genPlus(iCode *ic) { if ((AOP_SIZE(IC_LEFT(ic)) == 2 || AOP_SIZE(IC_RIGHT(ic)) == 2) && (AOP_SIZE(IC_LEFT(ic)) <= 2 && AOP_SIZE(IC_RIGHT(ic)) <= 2 || size == 2)) { - if (getPairId(AOP(IC_RIGHT(ic))) == PAIR_BC || - getPairId(AOP(IC_RIGHT(ic))) == PAIR_DE) { + if (getPairId(IC_RIGHT(ic)->aop) == PAIR_BC || + getPairId(IC_RIGHT(ic)->aop) == PAIR_DE && + getPairId(IC_LEFT(ic)->aop) != PAIR_BC) { /* Swap left and right */ operand *t = IC_RIGHT(ic); IC_RIGHT(ic) = IC_LEFT(ic); @@ -4830,11 +5604,15 @@ static void genPlus(iCode *ic) { } if (getPairId(AOP(IC_LEFT(ic))) == PAIR_BC) { fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); - emit2("add hl,bc"); + emit2("add hl, bc"); regalloc_dry_run_cost += 1; } else { + if (!isPairDead(PAIR_DE, ic)) + _push(PAIR_DE); + if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && - AOP_TYPE(IC_LEFT(ic)) == AOP_REG) { + AOP_SIZE(IC_RIGHT(ic)) == 2 && AOP_TYPE(IC_LEFT(ic)) == AOP_REG && + AOP_SIZE(IC_LEFT(ic)) == 2) { const short dst[4] = {E_IDX, L_IDX, D_IDX, H_IDX}; short src[4]; if (AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX || @@ -4849,7 +5627,7 @@ static void genPlus(iCode *ic) { src[3] = AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx; src[2] = AOP(IC_LEFT(ic))->aopu.aop_reg[1]->rIdx; } - regMove(dst, src, size, FALSE); + regMove(dst, src, 4, FALSE); } else if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && (AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX || AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == D_IDX || @@ -4857,17 +5635,24 @@ static void genPlus(iCode *ic) { (AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == E_IDX || AOP(IC_RIGHT(ic))->aopu.aop_reg[1]->rIdx == D_IDX))) { + emit2(";b"); fetchPair(PAIR_DE, AOP(IC_RIGHT(ic))); fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); } else { + emit2(";c"); fetchPair(PAIR_DE, AOP(IC_LEFT(ic))); fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); } - emit2("add hl,de"); + emit2("add hl, de"); regalloc_dry_run_cost += 1; + + if (!isPairDead(PAIR_DE, ic)) + _pop(PAIR_DE); } spillPair(PAIR_HL); - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + genMove(IC_RESULT(ic)->aop, ASMOP_HL, + !bitVectBitValue(ic->rSurv, A_IDX), true, + isPairDead(PAIR_DE, ic)); goto release; } } @@ -4880,47 +5665,350 @@ static void genPlus(iCode *ic) { } } - // Avoid overwriting operand in h or l when setupToPreserveCarry () loads hl. - if (!couldDestroyCarry(AOP(IC_LEFT(ic)))) { - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - premoved = TRUE; - } else - premoved = FALSE; + // Avoid overwriting operand in h or l when setupToPreserveCarry () loads hl - + // only necessary if carry is actually used during addition. + premoved = FALSE; + if (size > 1 && + !(size == 2 && (isPair(leftop) && rightop->type == AOP_LIT))) { + if (!couldDestroyCarry(leftop) && + (couldDestroyCarry(rightop) || couldDestroyCarry(AOP(IC_RESULT(ic))))) { + cheapMove(ASMOP_A, 0, leftop, offset, true); + premoved = TRUE; + } - setupToPreserveCarry(ic); + setupToPreserveCarry(AOP(IC_RESULT(ic)), leftop, rightop); + } + // But if we don't actually want to use hl for the addition, it can make sense + // to setup an op to use cheaper hl instead of iy. + if (size == 1 && !aopInReg(leftop, 0, H_IDX) && !aopInReg(leftop, 0, L_IDX) && + isPairDead(PAIR_HL, ic)) { + if (couldDestroyCarry(AOP(IC_RESULT(ic))) && + (AOP(IC_RESULT(ic)) == leftop || AOP(IC_RESULT(ic)) == rightop)) + shiftIntoPair(PAIR_HL, AOP(IC_RESULT(ic))); + else if (couldDestroyCarry(rightop)) + shiftIntoPair(PAIR_HL, rightop); + } cached[0] = -1; cached[1] = -1; - while (size--) { - if (!premoved) - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - else - premoved = FALSE; - if (offset == 0) { - if (size == 0 && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT && - ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit) == 1) - emit3(A_INC, ASMOP_A, 0); + + for (i = 0, started = FALSE; i < size;) { + // Addition of interleaved pairs. + if ((!premoved || i) && aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + leftop->size - i >= 2 && rightop->size - i >= 2) { + PAIR_ID pair = PAIR_INVALID; + + if (aopInReg(leftop, i, L_IDX) && aopInReg(rightop, i + 1, H_IDX)) { + if (aopInReg(leftop, i + 1, D_IDX) && aopInReg(rightop, i, E_IDX)) + pair = PAIR_DE; + else if (aopInReg(leftop, i + 1, B_IDX) && aopInReg(rightop, i, C_IDX)) + pair = PAIR_BC; + } else if (aopInReg(leftop, i + 1, H_IDX) && + aopInReg(rightop, i, L_IDX)) { + if (aopInReg(leftop, i, E_IDX) && aopInReg(rightop, i + 1, D_IDX)) + pair = PAIR_DE; + else if (aopInReg(leftop, i, C_IDX) && aopInReg(rightop, i + 1, B_IDX)) + pair = PAIR_BC; + } + + if (pair != PAIR_INVALID) { + if (started) { + emit2("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } else { + emit2("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + i += 2; + continue; + } + } + + if ((!premoved || i) && !started && i == size - 2 && !i && + isPair(AOP(IC_RIGHT(ic))) && AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD && + getPairId(AOP(IC_RIGHT(ic))) != PAIR_HL && isPairDead(PAIR_HL, ic)) { + fetchPair(PAIR_HL, AOP(IC_LEFT(ic))); + emit2("add hl, %s", getPairName(AOP(IC_RIGHT(ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + spillPair(PAIR_HL); + commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + i += 2; + } else if ((!premoved || i) && !started && i == size - 2 && !i && + isPair(AOP(IC_LEFT(ic))) && + (rightop->type == AOP_LIT || rightop->type == AOP_IMMD) && + getPairId(AOP(IC_LEFT(ic))) != PAIR_HL && + isPairDead(PAIR_HL, ic)) { + fetchPair(PAIR_HL, AOP(IC_RIGHT(ic))); + emit2("add hl, %s", getPairName(AOP(IC_LEFT(ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + spillPair(PAIR_HL); + commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + i += 2; + } else if ((!premoved || i) && !started && i == size - 2 && !i && + aopInReg(leftop, i, HL_IDX) && isPair(AOP(IC_RIGHT(ic))) && + isPairDead(PAIR_HL, ic)) { + emit2("add hl, %s", getPairName(AOP(IC_RIGHT(ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + i += 2; + } else if ((!premoved || i) && !started && i == size - 2 && !i && + isPair(AOP(IC_LEFT(ic))) && aopInReg(rightop, i, HL_IDX) && + isPairDead(PAIR_HL, ic)) { + emit2("add hl, %s", getPairName(AOP(IC_LEFT(ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + i += 2; + } else if ((!premoved || i) && !started && i == size - 2 && + aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(rightop, i, C_IDX) && + !bitVectBitValue(ic->rSurv, B_IDX)) { + if (aopInReg(rightop, i + 1, H_IDX) || aopInReg(rightop, i + 1, L_IDX)) { + cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), i + 1, true); + fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, i); + } else { + fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, i); + cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), i + 1, true); + } + emit2("add hl, bc"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } else if (!options.oldralloc && (!premoved || i) && !started && + i == size - 2 && aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(leftop, i, C_IDX) && + !bitVectBitValue(ic->rSurv, B_IDX)) { + if (aopInReg(leftop, i + 1, H_IDX) || aopInReg(leftop, i + 1, L_IDX)) { + cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), i + 1, true); + fetchPairLong(PAIR_HL, AOP(IC_RIGHT(ic)), 0, i); + } else { + fetchPairLong(PAIR_HL, AOP(IC_RIGHT(ic)), 0, i); + cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), i + 1, true); + } + emit2("add hl, bc"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } else if (!options.oldralloc && (!premoved || i) && !started && + i == size - 2 && aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(rightop, i, E_IDX) && + !bitVectBitValue(ic->rSurv, D_IDX)) { + if (aopInReg(rightop, i + 1, H_IDX) || aopInReg(rightop, i + 1, L_IDX)) { + cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), i + 1, true); + fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, i); + } else { + fetchPairLong(PAIR_HL, AOP(IC_LEFT(ic)), 0, i); + cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), i + 1, true); + } + emit2("add hl, de"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } else if (!options.oldralloc && (!premoved || i) && !started && + i == size - 2 && aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(leftop, i, E_IDX) && + !bitVectBitValue(ic->rSurv, D_IDX)) { + if (aopInReg(leftop, i + 1, H_IDX) || aopInReg(leftop, i + 1, L_IDX)) { + cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), i + 1, true); + fetchPairLong(PAIR_HL, AOP(IC_RIGHT(ic)), 0, i); + } else { + fetchPairLong(PAIR_HL, AOP(IC_RIGHT(ic)), 0, i); + cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), i + 1, true); + } + emit2("add hl, de"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } + // When adding a literal, the 16 bit addition results in smaller, faster + // code than two 8-bit additions. + else if ((!premoved || i) && aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(leftop, i, HL_IDX) && + (rightop->type == AOP_LIT && !aopIsLitVal(rightop, i, 1, 0) || + rightop->type == AOP_IMMD)) { + PAIR_ID pair = getFreePairId(ic); + bool pair_alive; + if (pair == PAIR_INVALID) + pair = PAIR_DE; + pair_alive = !isPairDead(pair, ic) || + IC_RESULT(ic)->aop->regs[_pairs[pair].l_idx] < i || + IC_RESULT(ic)->aop->regs[_pairs[pair].h_idx] < i || + IC_LEFT(ic)->aop->regs[_pairs[pair].l_idx] >= i + 2 || + IC_LEFT(ic)->aop->regs[_pairs[pair].h_idx] >= i + 2; + if (pair_alive) + _push(pair); + fetchPairLong(pair, IC_RIGHT(ic)->aop, 0, i); + if (started) { + emit2("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } else { + emit2("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + regalloc_dry_run_cost += 1; + if (pair_alive) + _pop(pair); + i += 2; + } + // When adding registers the 16 bit addition results in smaller, faster code + // than an 8-bit addition. + else if ((!premoved || i) && i == size - 1 && isPairDead(PAIR_HL, ic) && + aopInReg(AOP(IC_RESULT(ic)), i, L_IDX) && + (aopInReg(leftop, i, L_IDX) || aopInReg(rightop, i, L_IDX)) && + (aopInReg(leftop, i, C_IDX) || aopInReg(rightop, i, C_IDX) || + aopInReg(leftop, i, E_IDX) || aopInReg(rightop, i, E_IDX))) { + PAIR_ID pair = (leftop->aopu.aop_reg[i]->rIdx == C_IDX || + rightop->aopu.aop_reg[i]->rIdx == C_IDX) + ? PAIR_BC + : PAIR_DE; + if (started) { + emit2("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } else { + emit2("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + i++; + } + // When adding a literal, the 16 bit addition results in smaller, slower + // code than an 8-bit addition. + else if ((!premoved || i) && optimize.codeSize && !started && + i == size - 1 && isPairDead(PAIR_HL, ic) && + rightop->type == AOP_LIT && + aopInReg(AOP(IC_RESULT(ic)), i, L_IDX) && + aopInReg(leftop, i, L_IDX) && + (!bitVectBitValue(ic->rSurv, C_IDX) || + !bitVectBitValue(ic->rSurv, E_IDX))) { + PAIR_ID pair = bitVectBitValue(ic->rSurv, C_IDX) ? PAIR_DE : PAIR_BC; + emit2("ld %s, !immedbyte", _pairs[pair].l, + ((unsigned int)ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit)) & 0xff); + emit2("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 3; + i++; + } + // Skip over this byte. + else if (!premoved && !started && + (leftop->type == AOP_REG || AOP(IC_RESULT(ic))->type == AOP_REG) && + aopIsLitVal(rightop, i, 1, 0)) { + cheapMove(AOP(IC_RESULT(ic)), i, leftop, i, true); + i++; + } + // Conditional 16-bit inc. + else if (i == size - 2 && started && aopIsLitVal(rightop, i, 2, 0) && + (aopInReg(AOP(IC_RESULT(ic)), i, BC_IDX) && + aopInReg(leftop, i, BC_IDX) || + aopInReg(AOP(IC_RESULT(ic)), i, DE_IDX) && + aopInReg(leftop, i, DE_IDX) || + aopInReg(AOP(IC_RESULT(ic)), i, HL_IDX) && + aopInReg(leftop, i, HL_IDX) || + aopInReg(AOP(IC_RESULT(ic)), i, IY_IDX) && + aopInReg(leftop, i, IY_IDX))) { + PAIR_ID pair = getPairId_o(leftop, i); + + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel(0); + + if (!regalloc_dry_run) + emit2("jp NC, !tlabel", labelKey2num(tlbl->key)); + regalloc_dry_run_cost += + 2; // Use cost of jr as the peephole optimizer can typically optimize + // this jp into jr. Do not emit jr directly to still allow + // jump-to-jump optimization. + emit2("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += (1 + (pair == PAIR_IY)); + i += 2; + } + // Conditional 8-bit inc. + else if (i == size - 1 && started && aopIsLitVal(rightop, i, 1, 0) && + !aopInReg(leftop, i, + A_IDX) && // adc a, #0 is cheaper than conditional inc. + (i < leftop->size && leftop->type == AOP_REG && + AOP(IC_RESULT(ic))->type == AOP_REG && + leftop->aopu.aop_reg[i]->rIdx == + AOP(IC_RESULT(ic))->aopu.aop_reg[i]->rIdx && + leftop->aopu.aop_reg[i]->rIdx != IYL_IDX && + leftop->aopu.aop_reg[i]->rIdx != IYH_IDX || + leftop->type == AOP_STK && leftop == AOP(IC_RESULT(ic)) || + leftop->type == AOP_PAIRPTR && + leftop->aopu.aop_pairId == PAIR_HL)) { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel(0); + if (!regalloc_dry_run) + emit2("jp NC, !tlabel", labelKey2num(tlbl->key)); + regalloc_dry_run_cost += + 2; // Use cost of jr as the peephole optimizer can typically optimize + // this jp into jr. Do not emit jr directly to still allow + // jump-to-jump optimization. + emit3_o(A_INC, leftop, i, 0, 0); + i++; + } else { + if (!premoved) + cheapMove(ASMOP_A, 0, leftop, i, true); else - emit3_o(A_ADD, ASMOP_A, 0, AOP(IC_RIGHT(ic)), offset); - } else - emit3_o(A_ADC, ASMOP_A, 0, AOP(IC_RIGHT(ic)), offset); - if (size && - (requiresHL(AOP(IC_RIGHT(ic))) && AOP_TYPE(IC_RIGHT(ic)) != AOP_REG || - requiresHL(AOP(IC_LEFT(ic))) && AOP_TYPE(IC_LEFT(ic)) != AOP_REG) && - AOP_TYPE(IC_RESULT(ic)) == AOP_REG && - (AOP(IC_RESULT(ic))->aopu.aop_reg[offset]->rIdx == L_IDX || - AOP(IC_RESULT(ic))->aopu.aop_reg[offset]->rIdx == H_IDX)) { - wassert(cached[0] == -1 || cached[1] == -1); - cached[cached[0] == -1 ? 0 : 1] = offset++; - _push(PAIR_AF); - } else - cheapMove(AOP(IC_RESULT(ic)), offset++, ASMOP_A, 0); + premoved = FALSE; + + // Can't handle overwritten operand in hl. + if (started && + (IC_RESULT(ic)->aop->type == AOP_EXSTK || + IC_RESULT(ic)->aop->type == AOP_PAIRPTR) && + requiresHL(IC_RESULT(ic)->aop) && + (aopInReg(leftop, i, L_IDX) || aopInReg(leftop, i, H_IDX) || + aopInReg(rightop, i, L_IDX) || aopInReg(rightop, i, H_IDX))) { + wassert(regalloc_dry_run); + regalloc_dry_run_cost += 1000; + } + + if (!started && aopIsLitVal(rightop, i, 1, 0)) + ; // Skip over this byte. + // We can use inc / dec only for the only, top non-zero byte, since it + // neither takes into account an existing carry nor does it update the + // carry. + else if (!started && i == size - 1 && + (aopIsLitVal(rightop, i, 1, 1) || + aopIsLitVal(rightop, i, 1, 255))) { + emit3(aopIsLitVal(rightop, i, 1, 1) ? A_INC : A_DEC, ASMOP_A, 0); + started = TRUE; + } else { + emit3_o(started ? A_ADC : A_ADD, ASMOP_A, 0, rightop, i); + started = TRUE; + } + + _G.preserveCarry = (i != size - 1); + if (size && + (requiresHL(rightop) && rightop->size > i + 1 && + rightop->type != AOP_REG || + (requiresHL(leftop) && leftop->size > i + 1) && + leftop->type != AOP_REG) && + AOP_TYPE(IC_RESULT(ic)) == AOP_REG && + (AOP(IC_RESULT(ic))->aopu.aop_reg[i]->rIdx == L_IDX || + AOP(IC_RESULT(ic))->aopu.aop_reg[i]->rIdx == H_IDX)) { + wassert(cached[0] == -1 || cached[1] == -1); + cached[cached[0] == -1 ? 0 : 1] = offset++; + _push(PAIR_AF); + } else + cheapMove(AOP(IC_RESULT(ic)), i, ASMOP_A, 0, true); + i++; + } } - for (size = 0; size < 2; size++) + + _G.preserveCarry = FALSE; + + if (tlbl) + emitLabel(tlbl); + + for (size = 1; size >= 0; size--) if (cached[size] != -1) { _pop(PAIR_AF); - cheapMove(AOP(IC_RESULT(ic)), cached[size], ASMOP_A, 0); + cheapMove(AOP(IC_RESULT(ic)), cached[size], ASMOP_A, 0, true); } + release: _G.preserveCarry = FALSE; freeAsmop(IC_LEFT(ic), NULL); @@ -4929,214 +6017,295 @@ static void genPlus(iCode *ic) { } /*-----------------------------------------------------------------*/ -/* genMinusDec :- does subtraction with deccrement if possible */ +/* genSubDec :- does subtraction with decrement if possible */ /*-----------------------------------------------------------------*/ -static bool genMinusDec(const iCode *ic) { +static bool genMinusDec(const iCode *ic, asmop *result, asmop *left, + asmop *right) { unsigned int icount; unsigned int size = getDataSize(IC_RESULT(ic)); /* will try to generate a decrement */ /* if the right side is not a literal we cannot */ - if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) - return FALSE; + if (right->type != AOP_LIT) + return false; /* if the literal value of the right hand side is greater than 4 then it is not worth it */ - if ((icount = (unsigned int)ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit)) > 2) - return FALSE; + if ((icount = (unsigned int)ulFromVal(right->aopu.aop_lit)) > 2) + return false; size = getDataSize(IC_RESULT(ic)); /* if decrement 16 bits in register */ - if (sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) && (size > 1) && - isPair(AOP(IC_RESULT(ic)))) { + if (sameRegs(left, result) && (size > 1) && isPair(result)) { + regalloc_dry_run_cost += icount * (1 + (getPairId(result) == PAIR_IY)); while (icount--) - emit2("dec %s", getPairName(AOP(IC_RESULT(ic)))); - return TRUE; + emit2("dec %s", getPairName(result)); + return true; } /* If result is a pair */ if (isPair(AOP(IC_RESULT(ic)))) { - movLeft2ResultLong(IC_LEFT(ic), 0, IC_RESULT(ic), 0, 0, 2); + fetchPair(getPairId(result), left); + regalloc_dry_run_cost += icount * (1 + (getPairId(result) == PAIR_IY)); while (icount--) if (!regalloc_dry_run) - emit2("dec %s", getPairName(AOP(IC_RESULT(ic)))); - regalloc_dry_run_cost += 1; - return TRUE; + emit2("dec %s", getPairName(result)); + return true; } /* if decrement 16 bits in register */ - if (sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) && size == 2 && - isPairDead(_getTempPairId(), ic)) { - fetchPair(_getTempPairId(), AOP(IC_RESULT(ic))); + if (sameRegs(left, result) && size == 2 && isPairDead(_getTempPairId(), ic) && + !(requiresHL(left) && _getTempPairId() == PAIR_HL)) { + fetchPair(_getTempPairId(), left); + regalloc_dry_run_cost += icount * (1 + (_getTempPairId() == PAIR_IY)); while (icount--) if (!regalloc_dry_run) emit2("dec %s", _getTempPairName()); - regalloc_dry_run_cost += 1; - commitPair(AOP(IC_RESULT(ic)), _getTempPairId(), ic, FALSE); + commitPair(result, _getTempPairId(), ic, false); - return TRUE; + return true; } /* if the sizes are greater than 1 then we cannot */ - if (AOP_SIZE(IC_RESULT(ic)) > 1 || AOP_SIZE(IC_LEFT(ic)) > 1) - return FALSE; + if (result->size > 1 || left->size > 1) + return false; /* we can if the aops of the left & result match or if they are in registers and the registers are the same */ - if (sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic)))) { + if (sameRegs(left, result)) { while (icount--) - emit3(A_DEC, AOP(IC_RESULT(ic)), 0); - return TRUE; + emit3(A_DEC, result, 0); + return true; } - if (AOP_TYPE(IC_RESULT(ic)) == AOP_REG) { - cheapMove(AOP(IC_RESULT(ic)), 0, AOP(IC_LEFT(ic)), 0); + if (result->type == AOP_REG) { + cheapMove(result, 0, left, 0, true); while (icount--) - emit3(A_DEC, AOP(IC_RESULT(ic)), 0); - return TRUE; + emit3(A_DEC, result, 0); + return true; } - return FALSE; + return false; } /*-----------------------------------------------------------------*/ -/* genMinus - generates code for subtraction */ +/* genSub - generates code for subtraction */ /*-----------------------------------------------------------------*/ -static void genMinus(const iCode *ic) { +static void genSub(const iCode *ic, asmop *result, asmop *left, asmop *right) { int size, offset = 0; - unsigned long lit = 0L; - - aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - aopOp(IC_RIGHT(ic), ic, FALSE, FALSE); - aopOp(IC_RESULT(ic), ic, TRUE, FALSE); + unsigned long long lit = 0L; /* special cases :- */ /* if both left & right are in bit space */ - if (AOP_TYPE(IC_LEFT(ic)) == AOP_CRY && AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { + if (left->type == AOP_CRY && right->type == AOP_CRY) { wassertl(0, "Tried to subtract two bits"); - goto release; + return; } /* if I can do an decrement instead of subtract then GOOD for ME */ - if (genMinusDec(ic) == TRUE) - goto release; + if (genMinusDec(ic, result, left, right) == TRUE) + return; size = getDataSize(IC_RESULT(ic)); - if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) { - } else { - lit = ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit); - lit = -(long)lit; + if (right->type == AOP_LIT) { + lit = ullFromVal(right->aopu.aop_lit); + lit = -(long long)lit; } /* Same logic as genPlus */ if (IS_GB) { - if (AOP_TYPE(IC_LEFT(ic)) == AOP_STK || AOP_TYPE(IC_RIGHT(ic)) == AOP_STK || - AOP_TYPE(IC_RESULT(ic)) == AOP_STK) { - if ((AOP_SIZE(IC_LEFT(ic)) == 2 || AOP_SIZE(IC_RIGHT(ic)) == 2) && - (AOP_SIZE(IC_LEFT(ic)) <= 2 && AOP_SIZE(IC_RIGHT(ic)) <= 2)) { - PAIR_ID left = getPairId(AOP(IC_LEFT(ic))); - PAIR_ID right = getPairId(AOP(IC_RIGHT(ic))); - - if (left == PAIR_INVALID && right == PAIR_INVALID) { - left = PAIR_DE; - right = PAIR_HL; - } else if (right == PAIR_INVALID) - right = PAIR_DE; - else if (left == PAIR_INVALID) - left = PAIR_DE; - - fetchPair(left, AOP(IC_LEFT(ic))); + if (left->type == AOP_STK || right->type == AOP_STK || + result->type == AOP_STK) { + if ((left->size == 2 || right->size == 2) && + (left->size <= 2 && right->size <= 2)) { + PAIR_ID leftp = getPairId(left); + PAIR_ID rightp = getPairId(right); + + if (leftp == PAIR_INVALID && rightp == PAIR_INVALID) { + leftp = PAIR_DE; + rightp = PAIR_HL; + } else if (rightp == PAIR_INVALID) + rightp = PAIR_DE; + else if (leftp == PAIR_INVALID) + leftp = PAIR_DE; + + fetchPair(leftp, left); /* Order is important. Right may be HL */ - fetchPair(right, AOP(IC_RIGHT(ic))); + fetchPair(rightp, right); if (!regalloc_dry_run) { - emit2("ld a,%s", _pairs[left].l); - emit2("sub a,%s", _pairs[right].l); - emit2("ld e,a"); - emit2("ld a,%s", _pairs[left].h); - emit2("sbc a,%s", _pairs[right].h); + emit2("ld a, %s", _pairs[leftp].l); + emit2("sub a, %s", _pairs[rightp].l); + emit2("ld e, a"); + emit2("ld a, %s", _pairs[leftp].h); + emit2("sbc a, %s", _pairs[rightp].h); } regalloc_dry_run_cost += 5; if (AOP_SIZE(IC_RESULT(ic)) > 1) - cheapMove(AOP(IC_RESULT(ic)), 1, ASMOP_A, 0); - cheapMove(AOP(IC_RESULT(ic)), 0, ASMOP_E, 0); - goto release; + cheapMove(AOP(IC_RESULT(ic)), 1, ASMOP_A, 0, true); + cheapMove(AOP(IC_RESULT(ic)), 0, ASMOP_E, 0, true); + return; } } if (size == 4) { /* Be paranoid on the GB with 4 byte variables due to how C can be trashed by lda hl,n(sp). */ - _gbz80_emitAddSubLong(ic, FALSE); - goto release; + _gbz80_emitAddSubLongLong(ic, left, right, FALSE); + return; + } + } + + setupToPreserveCarry(result, left, right); + + /* if literal right, add a, #-lit, else normal subb */ + while (size) { + if (!IS_GB && + isPairDead(PAIR_HL, ic) && !(IS_RAB && offset && aopIsLitVal(left, offset, 2, 0x0000) /* Ugly workaround for fetchPairLong using bool hl, which destroys carry */) && + (aopInReg(result, offset, HL_IDX) || + aopInReg(result, offset, DE_IDX) && + (result->regs[L_IDX] < 0 || result->regs[L_IDX] > offset) && + (result->regs[H_IDX] < 0 || result->regs[H_IDX] > offset)) && + (aopInReg(left, offset, HL_IDX) || left->type == AOP_LIT || + left->type == AOP_IY) && + (aopInReg(right, offset, BC_IDX) || aopInReg(right, offset, DE_IDX) || + ((right->type == AOP_IY || right->type == AOP_HL) && + getFreePairId(ic) != PAIR_INVALID))) { + PAIR_ID rightpair; + + if (left->type == AOP_LIT || left->type == AOP_IY) + fetchPairLong(PAIR_HL, left, ic, offset); + if (right->type == AOP_IY || right->type == AOP_HL) { + rightpair = getFreePairId(ic); + fetchPairLong(rightpair, right, ic, offset); + } else + rightpair = getPartPairId(right, offset); + + if (!offset) + emit3(A_CP, ASMOP_A, ASMOP_A); + emit2("sbc hl, %s", _pairs[rightpair].name); + regalloc_dry_run_cost += 2; + if (aopInReg(result, offset, DE_IDX)) { + emit2("ex de, hl"); + regalloc_dry_run_cost++; + } + offset += 2; + size -= 2; + _G.preserveCarry = !!size; + continue; + } + + if (right->type != AOP_LIT) { + if (!offset) { + if (left->type == AOP_LIT && + byteOfVal(left->aopu.aop_lit, offset) == 0x00 && + aopInReg(right, offset, A_IDX)) + emit3(A_NEG, 0, 0); + else { + if (left->type == AOP_LIT && + byteOfVal(left->aopu.aop_lit, offset) == 0x00) + emit3(A_XOR, ASMOP_A, ASMOP_A); + else + cheapMove(ASMOP_A, 0, left, offset, true); + emit3_o(A_SUB, ASMOP_A, 0, right, offset); + } + } else { + cheapMove(ASMOP_A, 0, left, offset, true); + emit3_o(A_SBC, ASMOP_A, 0, right, offset); + } + } else { + cheapMove(ASMOP_A, 0, left, offset, true); + + /* first add without previous c */ + if (!offset) { + if (size == 0 && (unsigned int)(lit & 0x0FFL) == 0xFF) + emit3(A_DEC, ASMOP_A, 0); + else { + if (!regalloc_dry_run) + emit2("add a, !immedbyte", (unsigned int)(lit & 0x0FFL)); + regalloc_dry_run_cost += 2; + } + } else + emit2("adc a, !immedbyte", + (unsigned int)((lit >> (offset * 8)) & 0x0FFL)); } + size--; + _G.preserveCarry = !!size; + cheapMove(result, offset++, ASMOP_A, 0, true); + } + + if (AOP_SIZE(IC_RESULT(ic)) == 3 && left->size == 3 && + !sameRegs(result, left)) { + wassertl(0, "Tried to subtract on a long pointer"); } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void genMinus(const iCode *ic) { + aopOp(IC_LEFT(ic), ic, FALSE, FALSE); + aopOp(IC_RIGHT(ic), ic, FALSE, FALSE); + aopOp(IC_RESULT(ic), ic, TRUE, FALSE); + + genSub(ic, AOP(IC_RESULT(ic)), AOP(IC_LEFT(ic)), AOP(IC_RIGHT(ic))); + + _G.preserveCarry = FALSE; + freeAsmop(IC_LEFT(ic), NULL); + freeAsmop(IC_RIGHT(ic), NULL); + freeAsmop(IC_RESULT(ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void genUminusFloat(operand *op, operand *result) { + emitDebug("; genUminusFloat"); + + /* for this we just need to flip the + first bit then copy the rest in place */ + + cheapMove(ASMOP_A, 0, AOP(op), MSB32, true); + + emit2("xor a,!immedbyte", 0x80); + regalloc_dry_run_cost += 2; + cheapMove(AOP(result), MSB32, ASMOP_A, 0, true); - setupToPreserveCarry(ic); + if (operandsEqu(result, op)) + return; - /* if literal, add a,#-lit, else normal subb */ - while (size) { - if (!IS_GB && getPartPairId(AOP(IC_LEFT(ic)), offset) == PAIR_HL && - getPartPairId(AOP(IC_RESULT(ic)), offset) == PAIR_HL && - (getPartPairId(AOP(IC_RIGHT(ic)), offset) == PAIR_BC || - getPartPairId(AOP(IC_RIGHT(ic)), offset) == PAIR_DE)) { - if (!offset) - emit3(A_CP, ASMOP_A, ASMOP_A); - emit2("sbc hl, %s", - _pairs[getPartPairId(AOP(IC_RIGHT(ic)), offset)].name); - regalloc_dry_run_cost += 2; - offset += 2; - size -= 2; - continue; - } + genMove_o(result->aop, 0, op->aop, 0, AOP_SIZE(op) - 1, + !aopInReg(result->aop, MSB32, A_IDX), false, false); +} - if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) { - if (!offset) { - if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && - byteOfVal(AOP(IC_LEFT(ic))->aopu.aop_lit, offset) == 0x00) - emit3(A_XOR, ASMOP_A, ASMOP_A); - else - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - emit3_o(A_SUB, ASMOP_A, 0, AOP(IC_RIGHT(ic)), offset); - } else { - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); - emit3_o(A_SBC, ASMOP_A, 0, AOP(IC_RIGHT(ic)), offset); - } - } else { - cheapMove(ASMOP_A, 0, AOP(IC_LEFT(ic)), offset); +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void genUminus(const iCode *ic) { + /* assign asmops */ + aopOp(IC_LEFT(ic), ic, FALSE, FALSE); + aopOp(IC_RESULT(ic), ic, TRUE, FALSE); - /* first add without previous c */ - if (!offset) { - if (size == 0 && (unsigned int)(lit & 0x0FFL) == 0xFF) - emit3(A_DEC, ASMOP_A, 0); - else { - if (!regalloc_dry_run) - emit2("add a,!immedbyte", (unsigned int)(lit & 0x0FFL)); - regalloc_dry_run_cost += 2; - } - } else - emit2("adc a,!immedbyte", - (unsigned int)((lit >> (offset * 8)) & 0x0FFL)); - } - cheapMove(AOP(IC_RESULT(ic)), offset, ASMOP_A, 0); - offset++; - size--; + /* if both in bit space then special + case */ + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && AOP_TYPE(IC_LEFT(ic)) == AOP_CRY) { + wassertl(0, "Left and right are in bit space"); + goto release; } - if (AOP_SIZE(IC_RESULT(ic)) == 3 && AOP_SIZE(IC_LEFT(ic)) == 3 && - !sameRegs(AOP(IC_RESULT(ic)), AOP(IC_LEFT(ic)))) { - wassertl(0, "Tried to subtract on a long pointer"); - } + if (IS_FLOAT(operandType(IC_LEFT(ic)))) + genUminusFloat(IC_LEFT(ic), IC_RESULT(ic)); + else + genSub(ic, AOP(IC_RESULT(ic)), ASMOP_ZERO, AOP(IC_LEFT(ic))); release: _G.preserveCarry = FALSE; freeAsmop(IC_LEFT(ic), NULL); - freeAsmop(IC_RIGHT(ic), NULL); freeAsmop(IC_RESULT(ic), NULL); } @@ -5145,10 +6314,14 @@ static void genMinus(const iCode *ic) { /*-----------------------------------------------------------------*/ static void genMultOneChar(const iCode *ic) { symbol *tlbl1, *tlbl2; - bool savedB = FALSE; + bool savedB = false; - asmop *result = AOP(IC_RESULT(ic)); - int resultsize = AOP_SIZE(IC_RESULT(ic)); + asmop *result = IC_RESULT(ic)->aop; + int resultsize = result->size; + + if (IC_LEFT(ic)->aop->size > 1 || IC_RIGHT(ic)->aop->size > 2) + wassertl(0, + "Large multiplication is handled through support function calls."); if (IS_GB) { wassertl( @@ -5156,19 +6329,21 @@ static void genMultOneChar(const iCode *ic) { return; } - if (IS_Z180 && AOP_TYPE(IC_RESULT(ic)) == AOP_REG) { - if ((resultsize > 1 ? result->aopu.aop_reg[1]->rIdx == B_IDX + if ((IS_Z180 || IS_EZ80_Z80 || IS_Z80N) && + AOP_TYPE(IC_RESULT(ic)) == AOP_REG) { + if (!IS_Z80N && + (resultsize > 1 ? result->aopu.aop_reg[1]->rIdx == B_IDX : !bitVectBitValue(ic->rSurv, B_IDX)) && result->aopu.aop_reg[0]->rIdx == C_IDX) { if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX || AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == B_IDX) { - cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), LSB); - cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), LSB); + cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), LSB, true); + cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), LSB, true); } else { - cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), LSB); - cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), LSB); + cheapMove(ASMOP_B, 0, AOP(IC_LEFT(ic)), LSB, true); + cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), LSB, true); } emit2("mlt bc"); regalloc_dry_run_cost += 2; @@ -5181,17 +6356,18 @@ static void genMultOneChar(const iCode *ic) { AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX || AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == D_IDX) { - cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), LSB); - cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), LSB); + cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), LSB, true); + cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), LSB, true); } else { - cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), LSB); - cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), LSB); + cheapMove(ASMOP_D, 0, AOP(IC_LEFT(ic)), LSB, true); + cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), LSB, true); } emit2("mlt de"); regalloc_dry_run_cost += 2; return; } - if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && + if (!IS_Z80N && AOP_TYPE(IC_LEFT(ic)) == AOP_REG && + AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && ((AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == H_IDX && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == L_IDX || AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == L_IDX && @@ -5205,7 +6381,10 @@ static void genMultOneChar(const iCode *ic) { } } - if (IS_RAB && isPairDead(PAIR_HL, ic) && isPairDead(PAIR_BC, ic)) { + if (IS_RAB && !IS_R2K && isPairDead(PAIR_HL, ic) && + isPairDead(PAIR_BC, ic)) // A wait state bug makes mul unuseable in most + // scenarios on the original Rabbit 2000. + { const bool save_de = (resultsize > 1 && bitVectBitValue(ic->rSurv, D_IDX) || bitVectBitValue(ic->rSurv, E_IDX) && @@ -5218,33 +6397,31 @@ static void genMultOneChar(const iCode *ic) { if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == E_IDX) - cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0); + cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0, true); else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == E_IDX) - cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), 0, true); else if (AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == C_IDX) - cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0, true); else if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == C_IDX) - cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0, true); else { - cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0, true); + cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0, true); } if (resultsize > 1) { - cheapMove(ASMOP_D, 0, ASMOP_ZERO, 0); - cheapMove(ASMOP_B, 0, ASMOP_D, 0); + cheapMove(ASMOP_D, 0, ASMOP_ZERO, 0, true); + cheapMove(ASMOP_B, 0, ASMOP_D, 0, true); } emit2("mul"); regalloc_dry_run_cost++; - if (resultsize > 1) - commitPair(result, PAIR_BC, ic, FALSE); - else - cheapMove(result, 0, ASMOP_C, 0); + genMove(result, resultsize > 1 ? ASMOP_BC : ASMOP_C, + bitVectBitValue(ic->rSurv, A_IDX), true, false); if (save_de) _pop(PAIR_DE); @@ -5256,9 +6433,10 @@ static void genMultOneChar(const iCode *ic) { _G.stack.pushedDE = TRUE; } if (IS_RAB && !isPairDead(PAIR_BC, ic) || - !IS_Z180 && (!options.oldralloc && bitVectBitValue(ic->rSurv, B_IDX) || - options.oldralloc && bitVectBitValue(ic->rMask, B_IDX) && - !(getPairId(AOP(IC_RESULT(ic))) == PAIR_BC))) { + !(IS_Z180 || IS_EZ80_Z80) && + (!options.oldralloc && bitVectBitValue(ic->rSurv, B_IDX) || + options.oldralloc && bitVectBitValue(ic->rMask, B_IDX) && + !(getPairId(AOP(IC_RESULT(ic))) == PAIR_BC))) { _push(PAIR_BC); savedB = TRUE; } @@ -5269,18 +6447,20 @@ static void genMultOneChar(const iCode *ic) { AOP_TYPE(IC_RIGHT(ic)) == AOP_REG && AOP(IC_RIGHT(ic))->aopu.aop_reg[0]->rIdx == H_IDX && !requiresHL(AOP(IC_LEFT(ic)))) { - cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_H, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0, true); + cheapMove(ASMOP_H, 0, AOP(IC_RIGHT(ic)), 0, true); } else { - cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0); - cheapMove(ASMOP_H, 0, AOP(IC_LEFT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0, true); + cheapMove(ASMOP_H, 0, AOP(IC_LEFT(ic)), 0, true); } - if (IS_Z180) { + if (IS_Z180 || IS_EZ80_Z80) { emit2("ld l, e"); emit2("mlt hl"); regalloc_dry_run_cost += 3; - } else if (IS_RAB) { + } else if (IS_RAB && !IS_R2K) // A wait state bug makes mul unuseable in most + // scenarios on the original Rabbit 2000. + { emit2("ld c, h"); emit2("ld d, !immedbyte", 0x00); emit2("ld b, d"); @@ -5295,9 +6475,9 @@ static void genMultOneChar(const iCode *ic) { emit2("ld d, l"); emit2("ld b, !immedbyte", 0x08); emitLabel(tlbl1); - emit2("add hl,hl"); - emit2("jp NC,!tlabel", labelKey2num(tlbl2->key)); - emit2("add hl,de"); + emit2("add hl, hl"); + emit2("jp NC, !tlabel", labelKey2num(tlbl2->key)); + emit2("add hl, de"); emitLabel(tlbl2); emit2("djnz !tlabel", labelKey2num(tlbl1->key)); regalloc_dry_run_cost += 12; @@ -5314,43 +6494,69 @@ static void genMultOneChar(const iCode *ic) { _G.stack.pushedDE = FALSE; } - if (result->type != AOP_HL) { - if (resultsize == 1) - cheapMove(result, 0, ASMOP_L, 0); - else - commitPair(result, PAIR_HL, ic, FALSE); + genMove(result, ASMOP_HL, !bitVectBitValue(ic->rSurv, A_IDX), true, + isPairDead(PAIR_DE, ic)); +} + +/*-----------------------------------------------------------------*/ +/* genMultTwoChar - generates code for 16x16->16 multiplication */ +/*-----------------------------------------------------------------*/ +static void genMultTwoChar(const iCode *ic) { + operand *left = IC_LEFT(ic); + operand *right = IC_RIGHT(ic); + wassert(IS_RAB && !IS_R2K); // mul instruction is broken on Rabbit 2000. + + bool save_bc = !isPairDead(PAIR_BC, ic); + bool save_de = !isPairDead(PAIR_DE, ic); + + if (save_bc) + _push(PAIR_BC); + if (save_de) + _push(PAIR_DE); + + if (getPairId(left->aop) == PAIR_BC || getPairId(right->aop) == PAIR_DE) { + if (right->aop->regs[C_IDX] >= 0 && right->aop->regs[B_IDX] >= 0) { + wassert(regalloc_dry_run); + regalloc_dry_run_cost += 100; + } + genMove(ASMOP_BC, left->aop, !bitVectBitValue(ic->rSurv, A_IDX), + right->aop->regs[L_IDX] < 0 && right->aop->regs[H_IDX] < 0, + right->aop->regs[E_IDX] < 0 && right->aop->regs[D_IDX] < 0); + genMove(ASMOP_DE, right->aop, !bitVectBitValue(ic->rSurv, A_IDX), true, + true); } else { - if (resultsize == 1) { - emit2("ld a, l"); - regalloc_dry_run_cost += 1; - cheapMove(result, 0, ASMOP_A, 0); - } else { - if (!isPairDead(PAIR_DE, ic)) { - _push(PAIR_DE); - _G.stack.pushedDE = TRUE; - } - emit2("ld e, l"); - emit2("ld d, h"); - regalloc_dry_run_cost += 2; - commitPair(result, PAIR_DE, ic, FALSE); - if (!isPairDead(PAIR_DE, ic)) { - _pop(PAIR_DE); - _G.stack.pushedDE = FALSE; - } + if (left->aop->regs[C_IDX] >= 0 && left->aop->regs[B_IDX] >= 0) { + wassert(regalloc_dry_run); + regalloc_dry_run_cost += 100; } + genMove(ASMOP_BC, right->aop, !bitVectBitValue(ic->rSurv, A_IDX), + left->aop->regs[L_IDX] < 0 && left->aop->regs[H_IDX] < 0, + left->aop->regs[E_IDX] < 0 && left->aop->regs[D_IDX] < 0); + genMove(ASMOP_DE, left->aop, !bitVectBitValue(ic->rSurv, A_IDX), true, + true); } + + emit2("mul"); + regalloc_dry_run_cost++; + + genMove(IC_RESULT(ic)->aop, ASMOP_BC, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), true); + + if (save_de) + _pop(PAIR_DE); + if (save_bc) + _pop(PAIR_BC); } /*-----------------------------------------------------------------*/ /* genMult - generates code for multiplication */ /*-----------------------------------------------------------------*/ static void genMult(iCode *ic) { - int val; - int count, i; + int val, i; /* If true then the final operation should be a subtract */ - bool active = FALSE; + bool active = false; bool byteResult; - bool add_in_hl = FALSE; + bool add_in_hl = false; int a_cost = 0, l_cost = 0; PAIR_ID pair; @@ -5373,7 +6579,11 @@ static void genMult(iCode *ic) { IC_LEFT(ic) = t; } - if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) { + if (IS_RAB && !IS_R2K && IC_RIGHT(ic)->aop->type != AOP_LIT && !byteResult && + IC_LEFT(ic)->aop->size == 2 && IC_RIGHT(ic)->aop->size == 2) { + genMultTwoChar(ic); + goto release; + } else if (IC_RIGHT(ic)->aop->type != AOP_LIT) { genMultOneChar(ic); goto release; } @@ -5384,7 +6594,8 @@ static void genMult(iCode *ic) { wassertl(val != 1, "Can't multiply by 1"); // Try to use mlt. - if (IS_Z180 && AOP_SIZE(IC_LEFT(ic)) == 1 && AOP_SIZE(IC_RIGHT(ic)) == 1 && + if ((IS_Z180 || IS_EZ80_Z80 || IS_Z80N) && AOP_SIZE(IC_LEFT(ic)) == 1 && + AOP_SIZE(IC_RIGHT(ic)) == 1 && (byteResult || SPEC_USIGN(getSpec(operandType(IC_LEFT(ic)))) && SPEC_USIGN(getSpec(operandType(IC_RIGHT(ic)))))) { pair = getPairId(AOP(IC_RESULT(ic))); @@ -5429,6 +6640,9 @@ static void genMult(iCode *ic) { AOP(IC_RESULT(ic))->aopu.aop_reg[0]->rIdx == C_IDX))) goto no_mlt; + if (IS_Z80N && pair != PAIR_DE) + goto no_mlt; + if (!isPairDead(pair, ic)) _push(pair); @@ -5436,29 +6650,29 @@ static void genMult(iCode *ic) { case PAIR_HL: if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == H_IDX) - cheapMove(ASMOP_L, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_L, 0, AOP(IC_RIGHT(ic)), 0, true); else { - cheapMove(ASMOP_L, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_H, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_L, 0, AOP(IC_LEFT(ic)), 0, true); + cheapMove(ASMOP_H, 0, AOP(IC_RIGHT(ic)), 0, true); } break; case PAIR_DE: if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == D_IDX) - cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_RIGHT(ic)), 0, true); else { - cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_E, 0, AOP(IC_LEFT(ic)), 0, true); + cheapMove(ASMOP_D, 0, AOP(IC_RIGHT(ic)), 0, true); } break; default: wassert(pair == PAIR_BC); if (AOP_TYPE(IC_LEFT(ic)) == AOP_REG && AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx == B_IDX) - cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_C, 0, AOP(IC_RIGHT(ic)), 0, true); else { - cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0); - cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), 0); + cheapMove(ASMOP_C, 0, AOP(IC_LEFT(ic)), 0, true); + cheapMove(ASMOP_B, 0, AOP(IC_RIGHT(ic)), 0, true); } break; } @@ -5467,9 +6681,10 @@ static void genMult(iCode *ic) { regalloc_dry_run_cost += 2; if (byteResult) - cheapMove( - AOP(IC_RESULT(ic)), 0, - pair == PAIR_HL ? ASMOP_L : (pair == PAIR_DE ? ASMOP_E : ASMOP_C), 0); + cheapMove(AOP(IC_RESULT(ic)), 0, + pair == PAIR_HL ? ASMOP_L + : (pair == PAIR_DE ? ASMOP_E : ASMOP_C), + 0, true); else commitPair(AOP(IC_RESULT(ic)), pair, ic, FALSE); @@ -5480,6 +6695,41 @@ static void genMult(iCode *ic) { } no_mlt: + if (IS_RAB && !IS_R2K && isPairDead(PAIR_DE, ic) && + isPairDead(PAIR_BC, + ic) && // mul might be cheaper than a series of additions. mul + // is broken on the original Rabbit 2000. + !byteResult && + (IC_LEFT(ic)->aop->size > 1 || + SPEC_USIGN(getSpec(operandType(IC_LEFT(ic)))))) { + int num_add = 0; + bool active = false; + i = val; + for (int count = 0; count < 16; count++) { + if (count != 0 && active) + num_add++; + else if (i & 0x8000u) { + active = true; + num_add += active; + } + i <<= 1; + } + + if (num_add > (optimize.codeSize ? 4 : 6)) { + if (getPairId(IC_LEFT(ic)->aop) == PAIR_BC) + fetchPair(PAIR_DE, IC_RIGHT(ic)->aop); + else { + fetchPairLong(PAIR_DE, IC_LEFT(ic)->aop, ic, 0); + fetchPair(PAIR_BC, IC_RIGHT(ic)->aop); + } + emit2("mul"); + regalloc_dry_run_cost++; + genMove(IC_RESULT(ic)->aop, ASMOP_BC, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); + goto release; + } + } + pair = PAIR_DE; if (getPairId(AOP(IC_LEFT(ic))) == PAIR_BC || (byteResult || !bitVectBitValue(ic->rSurv, B_IDX)) && @@ -5500,9 +6750,9 @@ static void genMult(iCode *ic) { /* Use 16-bit additions even for 8-bit result when the operands are in the * right places. */ if (byteResult) { - if (AOP_TYPE(IC_LEFT(ic)) != AOP_ACC) + if (!aopInReg(IC_LEFT(ic)->aop, 0, A_IDX)) a_cost += ld_cost(ASMOP_A, AOP(IC_LEFT(ic))); - if (AOP_TYPE(IC_RESULT(ic)) != AOP_ACC) + if (!aopInReg(IC_RESULT(ic)->aop, 0, A_IDX)) a_cost += ld_cost(AOP(IC_RESULT(ic)), ASMOP_A); if (AOP_TYPE(IC_LEFT(ic)) != AOP_REG || AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx != L_IDX) @@ -5511,25 +6761,24 @@ static void genMult(iCode *ic) { AOP(IC_RESULT(ic))->aopu.aop_reg[0]->rIdx != L_IDX) l_cost += ld_cost(AOP(IC_RESULT(ic)), ASMOP_L); } - add_in_hl = (!byteResult || isPairDead(PAIR_HL, ic) && - !bitVectBitValue(ic->rSurv, D_IDX) && - l_cost < a_cost); + add_in_hl = (!byteResult || isPairDead(PAIR_HL, ic) && l_cost < a_cost); if (byteResult) { - cheapMove(add_in_hl ? ASMOP_L : ASMOP_A, 0, AOP(IC_LEFT(ic)), 0); + cheapMove(add_in_hl ? ASMOP_L : ASMOP_A, 0, AOP(IC_LEFT(ic)), 0, true); if (AOP_TYPE(IC_LEFT(ic)) != AOP_REG || AOP(IC_LEFT(ic))->aopu.aop_reg[0]->rIdx != (pair == PAIR_BC ? C_IDX : E_IDX)) cheapMove(pair == PAIR_BC ? ASMOP_C : ASMOP_E, 0, - add_in_hl ? ASMOP_L : ASMOP_A, 0); + add_in_hl ? ASMOP_L : ASMOP_A, 0, true); } else if (AOP_SIZE(IC_LEFT(ic)) == 1 && !SPEC_USIGN(getSpec(operandType(IC_LEFT(ic))))) { - cheapMove(pair == PAIR_BC ? ASMOP_C : ASMOP_E, 0, AOP(IC_LEFT(ic)), 0); + cheapMove(pair == PAIR_BC ? ASMOP_C : ASMOP_E, 0, AOP(IC_LEFT(ic)), 0, + true); emit2("ld a, %s", _pairs[pair].l); - emit2("rlc a"); + emit2("rlca"); emit2("sbc a, a"); emit2("ld %s, a", _pairs[pair].h); - regalloc_dry_run_cost += 5; + regalloc_dry_run_cost += 4; emit2("ld l, %s", _pairs[pair].l); emit2("ld h, %s", _pairs[pair].h); regalloc_dry_run_cost += 2; @@ -5543,8 +6792,7 @@ static void genMult(iCode *ic) { } i = val; - - for (count = 0; count < 16; count++) { + for (int count = 0; count < 16; count++) { if (count != 0 && active) { if (!add_in_hl) emit2("add a, a"); @@ -5552,7 +6800,7 @@ static void genMult(iCode *ic) { emit2("add hl, hl"); regalloc_dry_run_cost += 1; } - if (i & 0x8000U) { + if (i & 0x8000u) { if (active) { if (!add_in_hl) emit2("add a, %s", _pairs[pair].l); @@ -5572,10 +6820,8 @@ static void genMult(iCode *ic) { _G.stack.pushedDE = FALSE; } - if (byteResult) - cheapMove(AOP(IC_RESULT(ic)), 0, add_in_hl ? ASMOP_L : ASMOP_A, 0); - else - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); + genMove(IC_RESULT(ic)->aop, add_in_hl ? ASMOP_HL : ASMOP_A, true, + add_in_hl || isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); release: freeAsmop(IC_LEFT(ic), NULL); @@ -5612,6 +6858,7 @@ static void genIfxJump(iCode *ic, char *jval) { jlbl = IC_TRUE(ic); if (!strcmp(jval, "a")) { emit3(A_OR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost++; inst = "NZ"; } else if (!strcmp(jval, "z")) { inst = "Z"; @@ -5631,7 +6878,7 @@ static void genIfxJump(iCode *ic, char *jval) { inst = "PE"; } else { /* The buffer contains the bit on A that we should test */ - emit2("bit %s,a", jval); + emit2("bit %s, a", jval); regalloc_dry_run_cost += 2; inst = "NZ"; } @@ -5640,6 +6887,7 @@ static void genIfxJump(iCode *ic, char *jval) { jlbl = IC_FALSE(ic); if (!strcmp(jval, "a")) { emit3(A_OR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost++; inst = "Z"; } else if (!strcmp(jval, "z")) { inst = "NZ"; @@ -5659,14 +6907,14 @@ static void genIfxJump(iCode *ic, char *jval) { inst = "PO"; } else { /* The buffer contains the bit on A that we should test */ - emit2("bit %s,a", jval); + emit2("bit %s, a", jval); regalloc_dry_run_cost += 2; inst = "Z"; } } /* Z80 can do a conditional long jump */ if (!regalloc_dry_run) - emit2("jp %s,!tlabel", inst, labelKey2num(jlbl->key)); + emit2("jp %s, !tlabel", inst, labelKey2num(jlbl->key)); regalloc_dry_run_cost += 3; /* mark the icode as generated */ @@ -5683,7 +6931,7 @@ static const char *_getPairIdName(PAIR_ID id) { return _pairs[id].name; } static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, int sign, const iCode *ic) { int size, offset = 0; - unsigned long lit = 0L; + unsigned long long lit = 0ull; bool result_in_carry = FALSE; int a_always_byte = -1; @@ -5703,25 +6951,22 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, save_b = bitVectBitValue(ic->rSurv, B_IDX); save_bc = (save_b && bitVectBitValue(ic->rSurv, C_IDX)); - save_a = - ((AOP_TYPE(left) == AOP_ACC) || - AOP_TYPE(left) == AOP_REG && - AOP(left)->aopu.aop_reg[0]->rIdx == B_IDX && save_b || - AOP_TYPE(left) == AOP_REG && - AOP(left)->aopu.aop_reg[0]->rIdx == C_IDX && !save_b && save_bc); + save_a = (aopInReg(left->aop, 0, A_IDX) || + aopInReg(left->aop, 0, B_IDX) && save_b || + aopInReg(left->aop, 0, C_IDX) && !save_b && save_bc); if (save_bc) _push(PAIR_BC); if (save_a) { - cheapMove(ASMOP_A, 0, AOP(right), 0); + cheapMove(ASMOP_A, 0, AOP(right), 0, true); _push(PAIR_AF); } else - cheapMove(ASMOP_A, 0, AOP(right), 0); - cheapMove(save_b ? ASMOP_C : ASMOP_B, 0, ASMOP_A, 0); + cheapMove(ASMOP_A, 0, AOP(right), 0, true); + cheapMove(save_b ? ASMOP_C : ASMOP_B, 0, ASMOP_A, 0, true); if (save_a) _pop(PAIR_AF); else - cheapMove(ASMOP_A, 0, AOP(left), 0); + cheapMove(ASMOP_A, 0, AOP(left), 0, true); emit3_o(A_SUB, ASMOP_A, 0, save_b ? ASMOP_C : ASMOP_B, offset); if (save_bc) _pop(PAIR_BC); @@ -5729,16 +6974,28 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, goto fix; } + // Preserve A if necessary + if (ifx && size == 1 && !sign && aopInReg(left->aop, 0, A_IDX) && + bitVectBitValue(ic->rSurv, A_IDX) && + (AOP_TYPE(right) == AOP_LIT || + AOP_TYPE(right) == AOP_REG && + AOP(right)->aopu.aop_reg[offset]->rIdx != IYL_IDX && + AOP(right)->aopu.aop_reg[offset]->rIdx != IYH_IDX || + AOP_TYPE(right) == AOP_STK)) { + emit3(A_CP, ASMOP_A, AOP(right)); + result_in_carry = TRUE; + goto release; + } + // On the Gameboy we can't afford to adjust HL as it may trash the carry. - if (size > 1 && IS_GB && + if (size > 1 && (IS_GB || IY_RESERVED) && left->aop->type != AOP_REG && + right->aop->type != AOP_REG && (requiresHL(AOP(right)) && requiresHL(AOP(left)))) { - // Pull left into DE and right into HL - if (!regalloc_dry_run) { - aopGet(AOP(left), LSB, FALSE); - emit2("ld d, h"); - emit2("ld e, l"); - aopGet(AOP(right), LSB, FALSE); - } + if (!isPairDead(PAIR_DE, ic)) + _push(PAIR_DE); + + pointPairToAop(PAIR_DE, left->aop, 0); + pointPairToAop(PAIR_HL, right->aop, 0); while (size--) { emit2("ld a, (de)"); @@ -5752,22 +7009,29 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, } offset++; } - if (sign) { + if (sign && IS_GB) { + wassert(isPairDead(PAIR_DE, ic)); emit2("ld a, (de)"); emit2("ld d, a"); emit2("ld e, (hl)"); regalloc_dry_run_cost += 3; } + + spillPair(PAIR_DE); + if (!isPairDead(PAIR_DE, ic)) + _pop(PAIR_DE); + spillPair(PAIR_HL); result_in_carry = TRUE; goto fix; } else if (size > 1 && IS_GB && - (requiresHL(AOP(right)) && !requiresHL(AOP(left)))) { + (requiresHL(right->aop) && right->aop->type != AOP_REG && + !requiresHL(left->aop))) { if (!regalloc_dry_run) aopGet(AOP(right), LSB, FALSE); while (size--) { - cheapMove(ASMOP_A, 0, AOP(left), offset); + cheapMove(ASMOP_A, 0, AOP(left), offset, true); emit2("%s a, (hl)", offset == 0 ? "sub" : "sbc"); regalloc_dry_run_cost += 1; @@ -5778,7 +7042,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, offset++; } if (sign) { - cheapMove(ASMOP_A, 0, AOP(left), offset - 1); + cheapMove(ASMOP_A, 0, AOP(left), offset - 1, true); emit2("ld d, a"); emit2("ld e, (hl)"); regalloc_dry_run_cost += 2; @@ -5787,7 +7051,8 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, result_in_carry = TRUE; goto fix; } else if (size > 1 && IS_GB && - (!requiresHL(AOP(right)) && requiresHL(AOP(left)))) { + (!requiresHL(right->aop) && requiresHL(left->aop) && + left->aop->type != AOP_REG)) { if (!regalloc_dry_run) aopGet(AOP(left), LSB, FALSE); @@ -5805,7 +7070,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, if (sign) { emit2("ld d, (hl)"); regalloc_dry_run_cost += 1; - cheapMove(ASMOP_A, 0, AOP(right), offset - 1); + cheapMove(ASMOP_A, 0, AOP(right), offset - 1, true); emit2("ld e, a"); regalloc_dry_run_cost += 1; } @@ -5814,18 +7079,18 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, goto fix; } - if (IS_GB && sign) { - cheapMove(ASMOP_A, 0, AOP(right), size - 1); - cheapMove(ASMOP_E, 0, ASMOP_A, 0); - cheapMove(ASMOP_A, 0, AOP(left), size - 1); - cheapMove(ASMOP_D, 0, ASMOP_A, 0); + if (IS_GB && sign && AOP_TYPE(right) != AOP_LIT) { + cheapMove(ASMOP_A, 0, AOP(right), size - 1, true); + cheapMove(ASMOP_E, 0, ASMOP_A, 0, true); + cheapMove(ASMOP_A, 0, AOP(left), size - 1, true); + cheapMove(ASMOP_D, 0, ASMOP_A, 0, true); } if (AOP_TYPE(right) == AOP_LIT) { - lit = ulFromVal(AOP(right)->aopu.aop_lit); + lit = ullFromVal(AOP(right)->aopu.aop_lit); /* optimize if(x < 0) or if(x >= 0) */ - if (lit == 0) { + if (lit == 0ull) { if (!sign) { /* No sign so it's always false */ emit3(A_CP, ASMOP_A, ASMOP_A); @@ -5841,7 +7106,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, return; } /* Just load in the top most bit */ - cheapMove(ASMOP_A, 0, AOP(left), AOP_SIZE(left) - 1); + cheapMove(ASMOP_A, 0, AOP(left), AOP_SIZE(left) - 1, true); if (!(AOP_TYPE(result) == AOP_CRY && AOP_SIZE(result)) && ifx) { genIfxJump(ifx, "7"); return; @@ -5856,7 +7121,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, goto release; } - while (!((lit >> (offset * 8)) & 0xff)) { + while (!((lit >> (offset * 8)) & 0xffull)) { size--; offset++; } @@ -5876,7 +7141,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, PAIR_ID litpair = (isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC); fetchPair(PAIR_HL, AOP(left)); emit2("ld %s, !immedbyte", _pairs[litpair].name, - (lit ^ 0x8000u) & 0xffffu); + (unsigned long)((lit ^ 0x8000u) & 0xffffu)); regalloc_dry_run_cost += 3; emit2("add hl, hl"); emit2("ccf"); @@ -5895,19 +7160,20 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, goto release; } - cheapMove(ASMOP_A, 0, AOP(left), offset); + cheapMove(ASMOP_A, 0, AOP(left), offset, true); if (size == 1) { emit2("xor a, !immedbyte", 0x80); regalloc_dry_run_cost += 2; } emit2("sub a, !immedbyte", - ((lit >> (offset * 8)) & 0xff) ^ (size == 1 ? 0x80 : 0x00)); + (unsigned long)(((lit >> (offset * 8)) & 0xff) ^ + (size == 1 ? 0x80 : 0x00))); regalloc_dry_run_cost += 2; size--; offset++; while (size--) { - cheapMove(ASMOP_A, 0, AOP(left), offset); + cheapMove(ASMOP_A, 0, AOP(left), offset, true); if (!size) { emit2("rla"); emit2("ccf"); @@ -5916,20 +7182,23 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, } /* Subtract through, propagating the carry */ emit2("sbc a, !immedbyte", - ((lit >> (offset++ * 8)) & 0xff) ^ (size ? 0x00 : 0x80)); + (unsigned long)(((lit >> (offset++ * 8)) & 0xff) ^ + (size ? 0x00 : 0x80))); regalloc_dry_run_cost += 2; } result_in_carry = TRUE; goto release; } } - if (!IS_GB && (!sign || size > 2) && - getPartPairId(AOP(left), offset) == PAIR_HL && + (getPartPairId(left->aop, offset) == PAIR_HL || + size == 2 && left->aop->type == AOP_IY) && isPairDead(PAIR_HL, ic) && - (getPartPairId(AOP(right), offset) == PAIR_DE || - getPartPairId(AOP(right), offset) == PAIR_BC)) { - emit3(A_CP, ASMOP_A, ASMOP_A); // Clear carry. + (getPartPairId(right->aop, offset) == PAIR_DE || + getPartPairId(right->aop, offset) == PAIR_BC)) { + if (left->aop->type == AOP_DIR || left->aop->type == AOP_IY) + fetchPair(PAIR_HL, left->aop); + emit3(A_XOR, ASMOP_A, ASMOP_A); // Clear carry. emit2("sbc hl, %s", _pairs[getPartPairId(AOP(right), offset)].name); regalloc_dry_run_cost += 2; size -= 2; @@ -5939,7 +7208,7 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, byteOfVal(AOP(left)->aopu.aop_lit, offset) == 0x00) emit3(A_XOR, ASMOP_A, ASMOP_A); else - cheapMove(ASMOP_A, 0, AOP(left), offset); + cheapMove(ASMOP_A, 0, AOP(left), offset, true); if (size > 1 && AOP_TYPE(left) == AOP_LIT) { emit3_o(A_CP, ASMOP_A, 0, AOP(right), offset); a_always_byte = byteOfVal(AOP(left)->aopu.aop_lit, offset); @@ -5951,21 +7220,23 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, /* Subtract through, propagating the carry */ while (size) { - if (!IS_GB && (!sign || size > 2) && - getPartPairId(AOP(left), offset) == PAIR_HL && - isPairDead(PAIR_HL, ic) && - (getPartPairId(AOP(right), offset) == PAIR_DE || - getPartPairId(AOP(right), offset) == PAIR_BC)) { + if (!IS_GB && (!sign || size > 2) && isPairDead(PAIR_HL, ic) && + (getPartPairId(left->aop, offset) == PAIR_HL || + left->aop->type == AOP_LIT && right->aop->regs[L_IDX] < offset && + right->aop->regs[H_IDX] < offset) && + (getPartPairId(right->aop, offset) == PAIR_DE || + getPartPairId(right->aop, offset) == PAIR_BC)) { + fetchPairLong(PAIR_HL, left->aop, 0, offset); emit2("sbc hl, %s", _pairs[getPartPairId(AOP(right), offset)].name); regalloc_dry_run_cost += 2; size -= 2; offset += 2; } else { - if (!(AOP_TYPE(left) == AOP_LIT && - byteOfVal(AOP(left)->aopu.aop_lit, offset) == a_always_byte)) - cheapMove(ASMOP_A, 0, AOP(left), offset); + if (!(left->aop->type == AOP_LIT && + byteOfVal(left->aop->aopu.aop_lit, offset) == a_always_byte)) + cheapMove(ASMOP_A, 0, left->aop, offset, true); a_always_byte = -1; - emit3_o(A_SBC, ASMOP_A, 0, AOP(right), offset); + emit3_o(A_SBC, ASMOP_A, 0, right->aop, offset); size--; offset++; } @@ -5978,7 +7249,8 @@ static void genCmp(operand *left, operand *right, operand *result, iCode *ifx, { if (!regalloc_dry_run) { symbol *tlbl = newiTempLabel(NULL); - emit2("jp PO, !tlabel", labelKey2num(tlbl->key)); + emit2(IS_RAB ? "jp LZ, !tlabel" : "jp PO, !tlabel", + labelKey2num(tlbl->key)); emit2("xor a, !immedbyte", 0x80); emitLabelSpill(tlbl); } @@ -6071,7 +7343,7 @@ static void genCmpGt(iCode *ic, iCode *ifx) { aopOp(right, ic, FALSE, FALSE); aopOp(result, ic, TRUE, FALSE); - setupToPreserveCarry(ic); + setupToPreserveCarry(AOP(result), AOP(left), AOP(right)); genCmp(right, left, result, ifx, sign, ic); @@ -6105,7 +7377,7 @@ static void genCmpLt(iCode *ic, iCode *ifx) { aopOp(right, ic, FALSE, FALSE); aopOp(result, ic, TRUE, FALSE); - setupToPreserveCarry(ic); + setupToPreserveCarry(AOP(result), AOP(left), AOP(right)); genCmp(left, right, result, ifx, sign, ic); @@ -6123,70 +7395,107 @@ static PAIR_ID gencjneshort(operand *left, operand *right, symbol *lbl, const iCode *ic) { int size = max(AOP_SIZE(left), AOP_SIZE(right)); int offset = 0; - unsigned long lit = 0L; + bool a_result = FALSE; + bool next_zero; /* Swap the left and right if it makes the computation easier */ - if (AOP_TYPE(left) == AOP_LIT) { + if (AOP_TYPE(left) == AOP_LIT || aopInReg(right->aop, 0, A_IDX)) { operand *t = right; right = left; left = t; } + /* Non-destructive compare */ + if (aopInReg(left->aop, 0, A_IDX) && bitVectBitValue(ic->rSurv, A_IDX) && + (AOP_TYPE(right) == AOP_LIT || + AOP_TYPE(right) == AOP_REG && + AOP(right)->aopu.aop_reg[offset]->rIdx != IYL_IDX && + AOP(right)->aopu.aop_reg[offset]->rIdx != IYH_IDX || + AOP_TYPE(right) == AOP_STK)) { + if (AOP_TYPE(right) == AOP_LIT && !byteOfVal(AOP(right)->aopu.aop_lit, 0)) + emit3(A_OR, ASMOP_A, ASMOP_A); + else + emit3(A_CP, ASMOP_A, AOP(right)); + if (!regalloc_dry_run) + emit2("jp NZ,!tlabel", labelKey2num(lbl->key)); + regalloc_dry_run_cost += 3; + } /* if the right side is a literal then anything goes */ - if (AOP_TYPE(right) == AOP_LIT) { - lit = ulFromVal(AOP(right)->aopu.aop_lit); - if (lit == 0) { - cheapMove(ASMOP_A, 0, AOP(left), offset); - if (size > 1) { - while (--size) - emit3_o(A_OR, ASMOP_A, 0, AOP(left), ++offset); - } else - emit3(A_OR, ASMOP_A, ASMOP_A); - if (!regalloc_dry_run) - emit2("jp NZ,!tlabel", labelKey2num(lbl->key)); - regalloc_dry_run_cost += 3; - } else { - while (size--) { - if ((AOP_TYPE(left) == AOP_ACC && !bitVectBitValue(ic->rSurv, A_IDX) || - AOP_TYPE(left) == AOP_REG && - AOP(left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && - AOP(left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && - !bitVectBitValue(ic->rSurv, - AOP(left)->aopu.aop_reg[offset]->rIdx)) && - byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0x01) { - if (!regalloc_dry_run) - emit2("dec %s", aopGet(AOP(left), offset, FALSE)); - regalloc_dry_run_cost++; - } else if ((AOP_TYPE(left) == AOP_ACC && - !bitVectBitValue(ic->rSurv, A_IDX) || - AOP_TYPE(left) == AOP_REG && - AOP(left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && - AOP(left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && - !bitVectBitValue( - ic->rSurv, - AOP(left)->aopu.aop_reg[offset]->rIdx)) && - byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0xff) { - if (!regalloc_dry_run) - emit2("inc %s", aopGet(AOP(left), offset, FALSE)); - regalloc_dry_run_cost++; - } else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - if (byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0x00) - emit3(A_OR, ASMOP_A, ASMOP_A); - else if (byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0x01) { - emit2("dec a"); - regalloc_dry_run_cost++; - } else if (byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0xff) { - emit2("inc a"); - regalloc_dry_run_cost++; - } else - emit3_o(A_SUB, ASMOP_A, 0, AOP(right), offset); + else if (AOP_TYPE(right) == AOP_LIT) { + while (size--) { + next_zero = size && !byteOfVal(AOP(right)->aopu.aop_lit, offset + 1); + + // Test for 0 can be done more efficiently using or + if (!byteOfVal(AOP(right)->aopu.aop_lit, offset)) { + if (!a_result) { + cheapMove(ASMOP_A, 0, AOP(left), offset, true); + emit3(A_OR, ASMOP_A, ASMOP_A); + } else + emit3_o(A_OR, ASMOP_A, 0, AOP(left), offset); + + a_result = TRUE; + } else if ((aopInReg(left->aop, 0, A_IDX) && + !bitVectBitValue(ic->rSurv, A_IDX) || + AOP_TYPE(left) == AOP_REG && + AOP(left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && + AOP(left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && + !bitVectBitValue( + ic->rSurv, AOP(left)->aopu.aop_reg[offset]->rIdx)) && + byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0x01 && + !next_zero) { + if (!regalloc_dry_run) + emit2("dec %s", aopGet(AOP(left), offset, FALSE)); + regalloc_dry_run_cost++; + a_result = aopInReg(left->aop, 0, A_IDX); + } else if (!bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] < offset && size && + byteOfVal(right->aop->aopu.aop_lit, offset) == 0xff && + (left->aop->type == AOP_REG || left->aop->type == AOP_STK) && + byteOfVal(right->aop->aopu.aop_lit, offset) == + byteOfVal(right->aop->aopu.aop_lit, offset + 1)) { + cheapMove(ASMOP_A, 0, left->aop, offset, true); + while (byteOfVal(right->aop->aopu.aop_lit, offset + 1) == 0xff && + size) { + emit3_o(A_AND, ASMOP_A, 0, left->aop, ++offset); + size--; } + emit3(A_INC, ASMOP_A, 0); + next_zero = size && !byteOfVal(AOP(right)->aopu.aop_lit, offset + 1); + a_result = true; + } else if ((aopInReg(left->aop, 0, A_IDX) && + !bitVectBitValue(ic->rSurv, A_IDX) || + AOP_TYPE(left) == AOP_REG && + AOP(left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && + AOP(left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && + !bitVectBitValue( + ic->rSurv, AOP(left)->aopu.aop_reg[offset]->rIdx)) && + byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0xff && + !next_zero) { + if (!regalloc_dry_run) + emit2("inc %s", aopGet(AOP(left), offset, FALSE)); + regalloc_dry_run_cost++; + a_result = aopInReg(left->aop, 0, A_IDX); + } else { + cheapMove(ASMOP_A, 0, left->aop, offset, true); + + if (byteOfVal(right->aop->aopu.aop_lit, offset) == 0x01) + emit3(A_DEC, ASMOP_A, 0); + else if (byteOfVal(right->aop->aopu.aop_lit, offset) == 0xff) + emit3(A_INC, ASMOP_A, 0); + else + emit3_o(A_SUB, ASMOP_A, 0, right->aop, offset); + + a_result = true; + } + + // Only emit jump now if there is no following test for 0 (which would + // just or to a current result in a) + if (!(next_zero && a_result)) { if (!regalloc_dry_run) emit2("jp NZ,!tlabel", labelKey2num(lbl->key)); regalloc_dry_run_cost += 3; - offset++; } + offset++; } } /* if the right side is in a register or @@ -6197,7 +7506,40 @@ static PAIR_ID gencjneshort(operand *left, operand *right, symbol *lbl, AOP_IS_PAIRPTR(right, PAIR_HL) || AOP_IS_PAIRPTR(right, PAIR_IX) || AOP_IS_PAIRPTR(right, PAIR_IY)) { while (size--) { - cheapMove(ASMOP_A, 0, AOP(left), offset); + if (aopInReg(right->aop, offset, A_IDX) || + aopInReg(right->aop, offset, HL_IDX) || + aopInReg(left->aop, offset, BC_IDX) || + aopInReg(left->aop, offset, DE_IDX)) { + operand *t = right; + right = left; + left = t; + } + + if (!IS_GB && isPairDead(PAIR_HL, ic) && + (aopInReg(left->aop, offset, HL_IDX) && + (aopInReg(right->aop, offset, BC_IDX) || + aopInReg(right->aop, offset, DE_IDX) || + getFreePairId(ic) != PAIR_INVALID) || + size == 1 && (aopInReg(right->aop, offset, BC_IDX) || + aopInReg(right->aop, offset, DE_IDX)))) { + PAIR_ID pair = getPairId_o(right->aop, offset); + if (pair == PAIR_INVALID) + pair = getFreePairId(ic); + + fetchPairLong(PAIR_HL, left->aop, ic, offset); + fetchPairLong(pair, right->aop, 0, offset); + emit3(A_CP, ASMOP_A, ASMOP_A); + emit2("sbc hl, %s", _pairs[pair].name); + if (!regalloc_dry_run) + emit2("jp NZ,!tlabel", labelKey2num(lbl->key)); + regalloc_dry_run_cost += 5; + + offset += 2; + size--; + continue; + } + + cheapMove(ASMOP_A, 0, AOP(left), offset, true); if (AOP_TYPE(right) == AOP_LIT && byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0) { emit3(A_OR, ASMOP_A, ASMOP_A); @@ -6230,7 +7572,7 @@ static PAIR_ID gencjneshort(operand *left, operand *right, symbol *lbl, _emitMove(_pairs[pair].l, aopGet(AOP(left), offset, FALSE)); else regalloc_dry_run_cost += ld_cost(ASMOP_E, AOP(left)); - cheapMove(ASMOP_A, 0, AOP(right), offset); + cheapMove(ASMOP_A, 0, AOP(right), offset, true); emit2("sub a,%s", _pairs[pair].l); regalloc_dry_run_cost += 1; if (!regalloc_dry_run) @@ -6281,9 +7623,6 @@ static void genCmpEq(iCode *ic, iCode *ifx) { IS_GB && AOP_TYPE(IC_LEFT(ic)) == AOP_STK || IS_GB && AOP_TYPE(IC_RIGHT(ic)) == AOP_STK); - emitDebug("; genCmpEq: left %u, right %u, result %u", AOP_SIZE(IC_LEFT(ic)), - AOP_SIZE(IC_RIGHT(ic)), AOP_SIZE(IC_RESULT(ic))); - /* Swap operands if it makes the operation easier. ie if: 1. Left is a literal. */ @@ -6423,7 +7762,7 @@ static void genOrOp(const iCode *ic) { symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel(0); _toBoolean(left, TRUE); if (!regalloc_dry_run) - emit2("jp NZ,!tlabel", labelKey2num(tlbl->key)); + emit2("jp NZ, !tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 3; _toBoolean(right, FALSE); if (!regalloc_dry_run) @@ -6490,13 +7829,17 @@ static void jmpTrueOrFalse(iCode *ic, symbol *tlbl) { static void genAnd(const iCode *ic, iCode *ifx) { operand *left, *right, *result; int size, offset = 0; - unsigned long lit = 0L; + unsigned long long lit = 0L; unsigned int bytelit = 0; aopOp((left = IC_LEFT(ic)), ic, FALSE, FALSE); aopOp((right = IC_RIGHT(ic)), ic, FALSE, FALSE); aopOp((result = IC_RESULT(ic)), ic, TRUE, FALSE); + bool pushed_a = false; + bool a_free = !bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + /* if left is a literal & right is not then exchange them */ if ((AOP_TYPE(left) == AOP_LIT && AOP_TYPE(right) != AOP_LIT) || (AOP_NEEDSACC(right) && !AOP_NEEDSACC(left))) { @@ -6506,20 +7849,14 @@ static void genAnd(const iCode *ic, iCode *ifx) { } /* if result = right then exchange them */ - if (sameRegs(AOP(result), AOP(right))) { + if (sameRegs(AOP(result), AOP(right)) && !AOP_NEEDSACC(left)) { operand *tmp = right; right = left; left = tmp; } - /* if right is bit then exchange them */ - if (AOP_TYPE(right) == AOP_CRY && AOP_TYPE(left) != AOP_CRY) { - operand *tmp = right; - right = left; - left = tmp; - } if (AOP_TYPE(right) == AOP_LIT) - lit = ulFromVal(AOP(right)->aopu.aop_lit); + lit = ullFromVal(AOP(right)->aopu.aop_lit); size = AOP_SIZE(result); @@ -6529,9 +7866,10 @@ static void genAnd(const iCode *ic, iCode *ifx) { } /* Make sure A is on the left to not overwrite it. */ - if (AOP_TYPE(right) == AOP_ACC || - isPair(AOP(right)) && (getPairId(AOP(right)) == PAIR_HL || - getPairId(AOP(right)) == PAIR_IY)) { + if (aopInReg(right->aop, 0, A_IDX) || + !aopInReg(left->aop, 0, A_IDX) && isPair(AOP(right)) && + (getPairId(AOP(right)) == PAIR_HL || + getPairId(AOP(right)) == PAIR_IY)) { operand *tmp = right; right = left; left = tmp; @@ -6553,7 +7891,7 @@ static void genAnd(const iCode *ic, iCode *ifx) { while (sizel) { char *jumpcond = "NZ"; - if ((bytelit = ((lit >> (offset * 8)) & 0x0fful)) == 0x00ul) { + if ((bytelit = ((lit >> (offset * 8)) & 0x0ffull)) == 0x00ull) { sizel--; offset++; continue; @@ -6562,7 +7900,7 @@ static void genAnd(const iCode *ic, iCode *ifx) { /* Testing for the border bits of the accumulator destructively is cheap. */ if ((isLiteralBit(bytelit) == 0 || isLiteralBit(bytelit) == 7) && - AOP_TYPE(left) == AOP_ACC && !bitVectBitValue(ic->rSurv, A_IDX)) { + aopInReg(left->aop, 0, A_IDX) && !bitVectBitValue(ic->rSurv, A_IDX)) { emit3(isLiteralBit(bytelit) == 0 ? A_RRCA : A_RLCA, 0, 0); jumpcond = "C"; sizel--; @@ -6573,7 +7911,7 @@ static void genAnd(const iCode *ic, iCode *ifx) { /* More combinations would be possible, but this one is the one that is common in the floating-point library. */ else if (AOP_TYPE(left) == AOP_REG && sizel >= 4 && - ((lit >> (offset * 8)) & 0xfffffffful) == 0x7ffffffful && + ((lit >> (offset * 8)) & 0xffffffffull) == 0x7fffffffull && !IS_GB && getPartPairId(AOP(left), offset) == PAIR_HL && isPairDead(PAIR_HL, ic) && IS_RAB && getPartPairId(AOP(left), offset + 2) == PAIR_DE && @@ -6592,7 +7930,7 @@ static void genAnd(const iCode *ic, iCode *ifx) { destructively is cheap. */ /* More combinations would be possible, but these are the common ones. */ else if (AOP_TYPE(left) == AOP_REG && sizel >= 2 && - ((lit >> (offset * 8)) & 0xfffful) == 0x7ffful && + ((lit >> (offset * 8)) & 0xffffull) == 0x7fffull && (!IS_GB && getPartPairId(AOP(left), offset) == PAIR_HL && isPairDead(PAIR_HL, ic) || IS_RAB && getPartPairId(AOP(left), offset) == PAIR_DE && @@ -6670,8 +8008,8 @@ static void genAnd(const iCode *ic, iCode *ifx) { } /* Non-destructive and when exactly one bit per byte is set. */ else if (isLiteralBit(bytelit) >= 0 && - (AOP_TYPE(left) == AOP_STK || AOP_TYPE(left) == AOP_ACC || - AOP_TYPE(left) == AOP_IY || + (AOP_TYPE(left) == AOP_STK || aopInReg(left->aop, 0, A_IDX) || + AOP_TYPE(left) == AOP_HL || AOP_TYPE(left) == AOP_IY || AOP_TYPE(left) == AOP_REG && AOP(left)->aopu.aop_reg[0]->rIdx != IYL_IDX)) { if (!regalloc_dry_run) @@ -6683,7 +8021,8 @@ static void genAnd(const iCode *ic, iCode *ifx) { offset++; } /* Z180 has non-destructive and. */ - else if (IS_Z180 && AOP_TYPE(left) == AOP_ACC && + else if ((IS_Z180 || IS_EZ80_Z80 || IS_Z80N) && + aopInReg(left->aop, 0, A_IDX) && bitVectBitValue(ic->rSurv, A_IDX) && bytelit != 0x0ff) { if (!regalloc_dry_run) emit2("tst a, %s", aopGet(AOP(right), 0, FALSE)); @@ -6693,7 +8032,14 @@ static void genAnd(const iCode *ic, iCode *ifx) { } /* Generic case, loading into accumulator and testing there. */ else { - cheapMove(ASMOP_A, 0, AOP(left), offset); + if (bitVectBitValue(ic->rSurv, A_IDX) || + left->aop->regs[A_IDX] > offset || + right->aop->regs[A_IDX] > offset) { + regalloc_dry_run_cost += 100; + wassert(regalloc_dry_run); + } + + cheapMove(ASMOP_A, 0, left->aop, offset, true); if (isLiteralBit(bytelit) == 0 || isLiteralBit(bytelit) == 7) { emit3(isLiteralBit(bytelit) == 0 ? A_RRCA : A_RLCA, 0, 0); jumpcond = "C"; @@ -6721,9 +8067,8 @@ static void genAnd(const iCode *ic, iCode *ifx) { } // if(left & literal) else { - if (ifx) { + if (ifx) jmpTrueOrFalse(ifx, tlbl); - } goto release; } outBitC(result); @@ -6747,73 +8092,141 @@ static void genAnd(const iCode *ic, iCode *ifx) { goto release; } - /* if left is same as result */ - if (sameRegs(AOP(result), AOP(left))) { - for (; size--; offset++) { - if (AOP_TYPE(right) == AOP_LIT) { - bytelit = (lit >> (offset * 8)) & 0x0FFL; - if (bytelit == 0x0FF) - continue; - else if (bytelit == 0) - aopPut3(AOP(result), offset, ASMOP_ZERO, 0); - else if (isLiteralBit(~bytelit & 0xffu) >= 0 && - AOP_TYPE(result) == AOP_REG) { - if (!regalloc_dry_run) - emit2("res %d, %s", isLiteralBit(~bytelit & 0xffu), - aopGet(AOP(result), offset, FALSE)); - regalloc_dry_run_cost += 2; - } else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - if (isLiteralBit(~bytelit & 0xffu) >= 0) { - emit2("res %d, a", isLiteralBit(~bytelit & 0xffu)); - regalloc_dry_run_cost += 2; - } else - emit3_o(A_AND, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(left), offset, ASMOP_A, 0); - } - } else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_AND, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(left), offset, ASMOP_A, 0); + wassertl(AOP_TYPE(result) != AOP_CRY, "Result of and is in a bit"); + + for (int i = 0; i < size;) { + const bool hl_free = + isPairDead(PAIR_HL, ic) && + (left->aop->regs[L_IDX] < i && + left->aop->regs[H_IDX] < i & right->aop->regs[L_IDX] < i && + right->aop->regs[H_IDX] < i) && + (result->aop->regs[L_IDX] < 0 || result->aop->regs[L_IDX] >= i) && + (result->aop->regs[H_IDX] < 0 || result->aop->regs[H_IDX] >= i); + + if (!bitVectBitValue(ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= i && + right->aop->regs[A_IDX] <= i && + (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= i)) + a_free = true; + + if (pushed_a && + (aopInReg(left->aop, i, A_IDX) || aopInReg(right->aop, i, A_IDX))) { + _pop(PAIR_AF); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + else + pushed_a = false; + } + + if (AOP_TYPE(right) == AOP_LIT) { + bytelit = byteOfVal(right->aop->aopu.aop_lit, i); + + if (bytelit == 0x00 || bytelit == 0xff) { + int end; + for (end = i; + end < size && byteOfVal(right->aop->aopu.aop_lit, end) == bytelit; + end++) + ; + genMove_o(result->aop, i, bytelit == 0x00 ? ASMOP_ZERO : left->aop, i, + end - i, a_free, hl_free, !isPairInUse(PAIR_DE, ic)); + if (result->aop->regs[A_IDX] >= i && result->aop->regs[A_IDX] < end) + a_free = false; + i = end; + continue; + } else if (isLiteralBit(~bytelit & 0xffu) >= 0 && + (AOP_TYPE(result) == AOP_REG || + left == right && (AOP_TYPE(result) == AOP_STK || + AOP_TYPE(result) == AOP_DIR))) { + cheapMove(result->aop, i, left->aop, i, a_free); + if (!regalloc_dry_run) + emit2("res %d, %s", isLiteralBit(~bytelit & 0xffu), + aopGet(result->aop, i, false)); + regalloc_dry_run_cost += 2; + if (aopInReg(result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } else if (IS_RAB && (aopInReg(result->aop, i, HL_IDX) && + (aopInReg(left->aop, i, HL_IDX) && + !isPairInUse(PAIR_DE, ic) || + aopInReg(left->aop, i, DE_IDX)) || + aopInReg(result->aop, i, H_IDX) && + aopInReg(result->aop, i + 1, L_IDX) && + (aopInReg(left->aop, i, H_IDX) && + aopInReg(left->aop, i + 1, L_IDX) && + !isPairInUse(PAIR_DE, ic) || + aopInReg(left->aop, i, D_IDX) && + aopInReg(left->aop, i + 1, E_IDX)))) { + unsigned short mask = + aopInReg(result->aop, i, L_IDX) + ? (bytelit + (byteOfVal(right->aop->aopu.aop_lit, i + 1) << 8)) + : (byteOfVal(right->aop->aopu.aop_lit, i + 1) + (bytelit << 8)); + bool mask_in_de = + (aopInReg(left->aop, i, L_IDX) | aopInReg(left->aop, i, H_IDX)); + emit2(mask_in_de ? "ld de, !immedword" : "ld hl, !immedword", mask); + emit2("and hl, de"); + regalloc_dry_run_cost += 4; + i += 2; + continue; } } - } else { - // left & result in different registers - if (AOP_TYPE(result) == AOP_CRY) { - wassertl(0, "Tried to AND where the result is in carry"); - } else { - for (; (size--); offset++) { - // normal case - // result = left & right - if (AOP_TYPE(right) == AOP_LIT) { - if ((bytelit = (int)((lit >> (offset * 8)) & 0x0FFL)) == 0x0FF) { - cheapMove(AOP(result), offset, AOP(left), offset); - continue; - } else if (bytelit == 0) { - aopPut3(AOP(result), offset, ASMOP_ZERO, 0); - continue; - } else if (isLiteralBit(~bytelit & 0xffu) >= 0 && - AOP_TYPE(result) == AOP_REG) { - cheapMove(AOP(result), offset, AOP(left), offset); - if (!regalloc_dry_run) - emit2("res %d, %s", isLiteralBit(~bytelit & 0xffu), - aopGet(AOP(result), offset, FALSE)); - regalloc_dry_run_cost += 2; - continue; - } - } - // faster than result <- left, anl result,right - // and better if result is SFR - if (AOP_TYPE(left) == AOP_ACC) - emit3_o(A_AND, ASMOP_A, 0, AOP(right), offset); - else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_AND, ASMOP_A, 0, AOP(right), offset); - } - cheapMove(AOP(result), offset, ASMOP_A, 0); + + if (IS_RAB) { + const bool this_byte_l = + aopInReg(result->aop, i, L_IDX) && + (aopInReg(left->aop, i, L_IDX) && aopInReg(left->aop, i, E_IDX) || + aopInReg(left->aop, i, E_IDX) && aopInReg(left->aop, i, L_IDX)); + const bool this_byte_h = + aopInReg(result->aop, i, H_IDX) && + (aopInReg(left->aop, i, H_IDX) && aopInReg(left->aop, i, D_IDX) || + aopInReg(left->aop, i, D_IDX) && aopInReg(left->aop, i, H_IDX)); + const bool next_byte_l = aopInReg(result->aop, i + 1, L_IDX) && + (aopInReg(left->aop, i + 1, L_IDX) && + aopInReg(left->aop, i + 1, E_IDX) || + aopInReg(left->aop, i + 1, E_IDX) && + aopInReg(left->aop, i + 1, L_IDX)); + const bool next_byte_h = aopInReg(result->aop, i + 1, H_IDX) && + (aopInReg(left->aop, i + 1, H_IDX) && + aopInReg(left->aop, i + 1, D_IDX) || + aopInReg(left->aop, i + 1, D_IDX) && + aopInReg(left->aop, i + 1, H_IDX)); + + const bool this_byte = this_byte_l || this_byte_h; + const bool next_byte = next_byte_l || next_byte_h; + + const bool next_byte_unused = + !bitVectBitValue(ic->rMask, this_byte_l ? H_IDX : L_IDX); + + if (this_byte && (next_byte || next_byte_unused)) { + emit2("and hl, de"); + regalloc_dry_run_cost++; + i += (1 + next_byte); + continue; } } + + if (!a_free) { + wassert(!pushed_a); + _push(PAIR_AF); + pushed_a = true; + a_free = true; + } + + // Use plain and in a. + if (aopInReg(right->aop, i, A_IDX)) + emit3_o(A_AND, ASMOP_A, 0, left->aop, i); + else { + cheapMove(ASMOP_A, 0, left->aop, i, true); + emit3_o(A_AND, ASMOP_A, 0, right->aop, i); + } + cheapMove(result->aop, i, ASMOP_A, 0, true); + + if (aopInReg(result->aop, i, A_IDX)) + a_free = false; + + i++; } + if (pushed_a) + _pop(PAIR_AF); release: freeAsmop(left, NULL); @@ -6827,13 +8240,17 @@ static void genAnd(const iCode *ic, iCode *ifx) { static void genOr(const iCode *ic, iCode *ifx) { operand *left, *right, *result; int size, offset = 0; - unsigned long lit = 0L; + unsigned long long lit = 0; int bytelit = 0; aopOp((left = IC_LEFT(ic)), ic, FALSE, FALSE); aopOp((right = IC_RIGHT(ic)), ic, FALSE, FALSE); aopOp((result = IC_RESULT(ic)), ic, TRUE, FALSE); + bool pushed_a = false; + bool a_free = !bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + /* if left is a literal & right is not then exchange them */ if ((AOP_TYPE(left) == AOP_LIT && AOP_TYPE(right) != AOP_LIT) || (AOP_NEEDSACC(right) && !AOP_NEEDSACC(left))) { @@ -6843,20 +8260,14 @@ static void genOr(const iCode *ic, iCode *ifx) { } /* if result = right then exchange them */ - if (sameRegs(AOP(result), AOP(right))) { + if (sameRegs(AOP(result), AOP(right)) && !AOP_NEEDSACC(left)) { operand *tmp = right; right = left; left = tmp; } - /* if right is bit then exchange them */ - if (AOP_TYPE(right) == AOP_CRY && AOP_TYPE(left) != AOP_CRY) { - operand *tmp = right; - right = left; - left = tmp; - } if (AOP_TYPE(right) == AOP_LIT) - lit = ulFromVal(AOP(right)->aopu.aop_lit); + lit = ullFromVal(AOP(right)->aopu.aop_lit); size = AOP_SIZE(result); @@ -6866,7 +8277,7 @@ static void genOr(const iCode *ic, iCode *ifx) { } /* Make sure A is on the left to not overwrite it. */ - if (AOP_TYPE(right) == AOP_ACC) { + if (aopInReg(right->aop, 0, A_IDX)) { operand *tmp = right; right = left; left = tmp; @@ -6886,9 +8297,21 @@ static void genOr(const iCode *ic, iCode *ifx) { } /* PENDING: Modeled after the AND code which is inefficient. */ while (sizel--) { - bytelit = (lit >> (offset * 8)) & 0x0FFL; + if (!bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= offset && + right->aop->regs[A_IDX] <= offset && + (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (!a_free) // Hard to handle pop with ifx + { + regalloc_dry_run_cost += 100; + wassert(regalloc_dry_run); + } + + bytelit = (lit >> (offset * 8)) & 0x0FFull; - cheapMove(ASMOP_A, 0, AOP(left), offset); + cheapMove(ASMOP_A, 0, left->aop, offset, true); if (bytelit != 0) emit3_o(A_OR, ASMOP_A, 0, AOP(right), offset); @@ -6904,68 +8327,153 @@ static void genOr(const iCode *ic, iCode *ifx) { regalloc_dry_run_cost += 3; } - offset++; + offset++; + } + if (ifx) { + jmpTrueOrFalse(ifx, tlbl); + } + goto release; + } + + wassertl(AOP_TYPE(result) != AOP_CRY, "Result of or is in a bit"); + + for (int i = 0; i < size;) { + if (!bitVectBitValue(ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= i && + right->aop->regs[A_IDX] <= i && + (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= i)) + a_free = true; + + if (pushed_a && + (aopInReg(left->aop, i, A_IDX) || aopInReg(right->aop, i, A_IDX))) { + _pop(PAIR_AF); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + else + pushed_a = false; + } + + if (AOP_TYPE(right) == AOP_LIT) { + bytelit = byteOfVal(right->aop->aopu.aop_lit, i); + + if (bytelit == 0xff) { + // TODO + } else if (bytelit == 0x00) { + cheapMove(result->aop, i, left->aop, i, a_free); + if (aopInReg(result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } else if (isLiteralBit(bytelit) >= 0 && + (AOP_TYPE(result) == AOP_REG || + left == right && (AOP_TYPE(result) == AOP_STK || + AOP_TYPE(result) == AOP_DIR))) { + cheapMove(result->aop, i, left->aop, i, a_free); + if (!regalloc_dry_run) + emit2("set %d, %s", isLiteralBit(bytelit), + aopGet(result->aop, i, false)); + regalloc_dry_run_cost += 2; + if (aopInReg(result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } else if (IS_RAB && (aopInReg(result->aop, i, HL_IDX) && + (aopInReg(left->aop, i, HL_IDX) && + !isPairInUse(PAIR_DE, ic) || + aopInReg(left->aop, i, DE_IDX)) || + aopInReg(result->aop, i, H_IDX) && + aopInReg(result->aop, i + 1, L_IDX) && + (aopInReg(left->aop, i, H_IDX) && + aopInReg(left->aop, i + 1, L_IDX) && + !isPairInUse(PAIR_DE, ic) || + aopInReg(left->aop, i, D_IDX) && + aopInReg(left->aop, i + 1, E_IDX)))) { + unsigned short mask = + aopInReg(result->aop, i, L_IDX) + ? (bytelit + (byteOfVal(right->aop->aopu.aop_lit, i + 1) << 8)) + : (byteOfVal(right->aop->aopu.aop_lit, i + 1) + (bytelit << 8)); + bool mask_in_de = + (aopInReg(left->aop, i, L_IDX) | aopInReg(left->aop, i, H_IDX)); + emit2(mask_in_de ? "ld de, !immedword" : "ld hl, !immedword", mask); + emit2("or hl, de"); + regalloc_dry_run_cost += 4; + i += 2; + continue; + } + } + + if (IS_RAB) { + const bool this_byte_l = + aopInReg(result->aop, i, L_IDX) && + (aopInReg(left->aop, i, L_IDX) && aopInReg(left->aop, i, E_IDX) || + aopInReg(left->aop, i, E_IDX) && aopInReg(left->aop, i, L_IDX)); + const bool this_byte_h = + aopInReg(result->aop, i, H_IDX) && + (aopInReg(left->aop, i, H_IDX) && aopInReg(left->aop, i, D_IDX) || + aopInReg(left->aop, i, D_IDX) && aopInReg(left->aop, i, H_IDX)); + const bool next_byte_l = aopInReg(result->aop, i + 1, L_IDX) && + (aopInReg(left->aop, i + 1, L_IDX) && + aopInReg(left->aop, i + 1, E_IDX) || + aopInReg(left->aop, i + 1, E_IDX) && + aopInReg(left->aop, i + 1, L_IDX)); + const bool next_byte_h = aopInReg(result->aop, i + 1, H_IDX) && + (aopInReg(left->aop, i + 1, H_IDX) && + aopInReg(left->aop, i + 1, D_IDX) || + aopInReg(left->aop, i + 1, D_IDX) && + aopInReg(left->aop, i + 1, H_IDX)); + + const bool this_byte = this_byte_l || this_byte_h; + const bool next_byte = next_byte_l || next_byte_h; + + const bool next_byte_unused = + !bitVectBitValue(ic->rMask, this_byte_l ? H_IDX : L_IDX); + + if (this_byte && (next_byte || next_byte_unused)) { + emit2("or hl, de"); + regalloc_dry_run_cost++; + i += (1 + next_byte); + continue; + } + + if (aopInReg(right->aop, i, DE_IDX) && + (left->aop->type == AOP_STK || left->aop->type == AOP_DIR || + left->aop->type == AOP_IY) && + (aopInReg(result->aop, i, HL_IDX) || + isPairDead(PAIR_HL, ic) && right->aop->regs[L_IDX] < i + 2 && + right->aop->regs[H_IDX] < i + 2 && + (result->aop->type == AOP_DIR || result->aop->type == AOP_IY || + result->aop->type == AOP_STK))) { + fetchPairLong(PAIR_HL, left->aop, ic, i); + emit2("or hl, de"); + regalloc_dry_run_cost++; + genMove_o(result->aop, i, ASMOP_HL, 0, 2, a_free, true, false); + i += 2; + continue; + } } - if (ifx) { - jmpTrueOrFalse(ifx, tlbl); + + // Use plain or in a. + if (!a_free) { + wassert(!pushed_a); + _push(PAIR_AF); + pushed_a = true; + a_free = true; } - goto release; - } - /* if left is same as result */ - if (sameRegs(AOP(result), AOP(left))) { - for (; size--; offset++) { - if (AOP_TYPE(right) == AOP_LIT) { - bytelit = (lit >> (offset * 8)) & 0x0FFL; - if (bytelit == 0x00L) - continue; - else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - if (isLiteralBit(bytelit) >= 0) - emit2("set %d, a", isLiteralBit(bytelit)); - else - emit3_o(A_OR, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); - } - } else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_OR, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); - } + if (aopInReg(right->aop, i, A_IDX)) + emit3_o(A_OR, ASMOP_A, 0, left->aop, i); + else { + cheapMove(ASMOP_A, 0, left->aop, i, true); + emit3_o(A_OR, ASMOP_A, 0, right->aop, i); } - } else { - // left & result in different registers - if (AOP_TYPE(result) == AOP_CRY) { - wassertl(0, "Result of OR is in a bit"); - } else - for (; (size--); offset++) { - // normal case - // result = left & right - if (AOP_TYPE(right) == AOP_LIT) { - if (((lit >> (offset * 8)) & 0x0FFL) == 0x00L) { - cheapMove(AOP(result), offset, AOP(left), offset); - continue; - } - } - // This is a bit broken, there will be problems for y_0 = x_0 | z_0, x_0 - // = y_0 | z_1 as either order of the two byte ors will result in the - // result destroying the second part of the operand. Currently, we use a - // workaround in the register allocator to avoid this. See also the - // comment on the 2-byte addition workaround above. - if (AOP_TYPE(left) != AOP_ACC) - cheapMove(ASMOP_A, 0, AOP(left), offset); - if (AOP_TYPE(right) == AOP_LIT && - isLiteralBit(((lit >> (offset * 8)) & 0x0FFL)) >= 0) - emit2("set %d, a", isLiteralBit(((lit >> (offset * 8)) & 0x0FFL))); - else - emit3_o(A_OR, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); - /* PENDING: something weird is going on here. Add exception. */ - if (AOP_TYPE(result) == AOP_ACC) - break; - } + cheapMove(result->aop, i, ASMOP_A, 0, true); + if (aopInReg(result->aop, i, A_IDX)) + a_free = false; + i++; } + if (pushed_a) + _pop(PAIR_AF); + release: freeAsmop(left, NULL); freeAsmop(right, NULL); @@ -6978,12 +8486,16 @@ static void genOr(const iCode *ic, iCode *ifx) { static void genXor(const iCode *ic, iCode *ifx) { operand *left, *right, *result; int size, offset = 0; - unsigned long lit = 0L; + unsigned long long lit = 0; + bool pushed_a = false; aopOp((left = IC_LEFT(ic)), ic, FALSE, FALSE); aopOp((right = IC_RIGHT(ic)), ic, FALSE, FALSE); aopOp((result = IC_RESULT(ic)), ic, TRUE, FALSE); + bool a_free = !bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + /* if left is a literal & right is not then exchange them */ if ((AOP_TYPE(left) == AOP_LIT && AOP_TYPE(right) != AOP_LIT) || (AOP_NEEDSACC(right) && !AOP_NEEDSACC(left))) { @@ -6993,20 +8505,14 @@ static void genXor(const iCode *ic, iCode *ifx) { } /* if result = right then exchange them */ - if (sameRegs(AOP(result), AOP(right))) { + if (sameRegs(result->aop, AOP(right)) && !AOP_NEEDSACC(left)) { operand *tmp = right; right = left; left = tmp; } - /* if right is bit then exchange them */ - if (AOP_TYPE(right) == AOP_CRY && AOP_TYPE(left) != AOP_CRY) { - operand *tmp = right; - right = left; - left = tmp; - } if (AOP_TYPE(right) == AOP_LIT) - lit = ulFromVal(AOP(right)->aopu.aop_lit); + lit = ullFromVal(AOP(right)->aopu.aop_lit); size = AOP_SIZE(result); @@ -7016,7 +8522,8 @@ static void genXor(const iCode *ic, iCode *ifx) { } /* Make sure A is on the left to not overwrite it. */ - if (AOP_TYPE(right) == AOP_ACC) { + if (aopInReg(right->aop, 0, A_IDX)) { + wassert(!AOP_NEEDSACC(left)); operand *tmp = right; right = left; left = tmp; @@ -7037,14 +8544,45 @@ static void genXor(const iCode *ic, iCode *ifx) { "into a bit"); } while (sizel--) { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); + if (!bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= offset && + right->aop->regs[A_IDX] <= offset && + (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (!a_free) { + wassert(!pushed_a); + _push(PAIR_AF); + a_free = true; + pushed_a = true; + if (ifx) // The pop at the end is hard to eal with in case of ifx. + { + regalloc_dry_run_cost += 100; + wassert(regalloc_dry_run); + } + } else if (pushed_a && (aopInReg(left->aop, offset, A_IDX) || + aopInReg(right->aop, offset, A_IDX))) { + _pop(PAIR_AF); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + else + pushed_a = false; + } + + if (aopInReg(right->aop, offset, A_IDX)) + emit3_o(A_XOR, ASMOP_A, 0, left->aop, offset); + else { + cheapMove(ASMOP_A, 0, left->aop, offset, true); + emit3_o(A_XOR, ASMOP_A, 0, right->aop, offset); + } if (ifx) /* emit jmp only, if it is actually used * */ if (!regalloc_dry_run) - emit2("jp NZ,!tlabel", labelKey2num(tlbl->key)); + emit2("jp NZ, !tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 3; offset++; } + if (pushed_a) + _pop(PAIR_AF); if (ifx) { jmpTrueOrFalse(ifx, tlbl); } else if (size) { @@ -7053,52 +8591,61 @@ static void genXor(const iCode *ic, iCode *ifx) { goto release; } - /* if left is same as result */ - if (sameRegs(AOP(result), AOP(left))) { - for (; size--; offset++) { - if (AOP_TYPE(right) == AOP_LIT) { - if (((lit >> (offset * 8)) & 0x0FFL) == 0x00L) + // left & result in different registers + if (AOP_TYPE(result) == AOP_CRY) { + wassertl(0, "Result of XOR is in a bit"); + } else + for (; (size--); offset++) { + if (!bitVectBitValue(ic->rSurv, A_IDX) && + left->aop->regs[A_IDX] <= offset && + right->aop->regs[A_IDX] <= offset && + (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (pushed_a && (aopInReg(left->aop, offset, A_IDX) || + aopInReg(right->aop, offset, A_IDX))) { + _pop(PAIR_AF); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + else + pushed_a = false; + } + + // normal case + // result = left & right + if (right->aop->type == AOP_LIT) { + if (((lit >> (offset * 8)) & 0x0FFL) == 0x00L) { + cheapMove(result->aop, offset, left->aop, offset, a_free); + if (aopInReg(result->aop, offset, A_IDX)) + a_free = false; continue; - else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); - } - } else { - if (AOP_TYPE(left) == AOP_ACC) - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); - else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); } } - } - } else { - // left & result in different registers - if (AOP_TYPE(result) == AOP_CRY) { - wassertl(0, "Result of XOR is in a bit"); - } else - for (; (size--); offset++) { - // normal case - // result = left & right - if (AOP_TYPE(right) == AOP_LIT) { - if (((lit >> (offset * 8)) & 0x0FFL) == 0x00L) { - cheapMove(AOP(result), offset, AOP(left), offset); - continue; - } - } - // faster than result <- left, anl result,right - // and better if result is SFR - if (AOP_TYPE(left) == AOP_ACC) - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); - else { - cheapMove(ASMOP_A, 0, AOP(left), offset); - emit3_o(A_XOR, ASMOP_A, 0, AOP(right), offset); - } - cheapMove(AOP(result), offset, ASMOP_A, 0); + // faster than result <- left, anl result,right + // and better if result is SFR + if (!a_free) { + wassert(!pushed_a); + _push(PAIR_AF); + a_free = true; + pushed_a = true; } - } + + if (aopInReg(right->aop, offset, A_IDX)) + emit3_o(A_XOR, ASMOP_A, 0, left->aop, offset); + else { + cheapMove(ASMOP_A, 0, left->aop, offset, true); + if (right->aop->type == AOP_LIT && + ((lit >> (offset * 8)) & 0xff) == 0xff) + emit3(A_CPL, 0, 0); + else + emit3_o(A_XOR, ASMOP_A, 0, right->aop, offset); + } + cheapMove(result->aop, offset, ASMOP_A, 0, true); + if (aopInReg(result->aop, offset, A_IDX)) + a_free = false; + } + if (pushed_a) + _pop(PAIR_AF); release: freeAsmop(left, NULL); @@ -7128,14 +8675,14 @@ static void genGetHbit(const iCode *ic) { aopOp(result, ic, FALSE, FALSE); /* get the highest order byte into a */ - cheapMove(ASMOP_A, 0, AOP(left), AOP_SIZE(left) - 1); + cheapMove(ASMOP_A, 0, left->aop, AOP_SIZE(left) - 1, true); if (AOP_TYPE(result) == AOP_CRY) { emit3(A_RL, ASMOP_A, 0); outBitC(result); } else { - emit3(A_RLC, ASMOP_A, 0); - emit2("and a,!one"); + emit3(A_RLCA, 0, 0); + emit2("and a, !one"); regalloc_dry_run_cost += 2; outAcc(result); } @@ -7200,16 +8747,15 @@ static void shiftR2Left2Result(const iCode *ic, operand *left, int offl, } emit2("and hl, de"); regalloc_dry_run_cost += 1; - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, TRUE); + genMove(IC_RESULT(ic)->aop, ASMOP_HL, true, true, isPairDead(PAIR_DE, ic)); return; } if (isPair(AOP(result)) && !offr) fetchPairLong(getPairId(AOP(result)), AOP(left), ic, offl); - else { - movLeft2Result(left, offl, result, offr, 0); - movLeft2Result(left, offl + 1, result, offr + 1, 0); - } + else + genMove_o(result->aop, offr, left->aop, offl, 2, true, + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); if (shCount == 0) return; @@ -7231,7 +8777,7 @@ static void shiftR2Left2Result(const iCode *ic, operand *left, int offl, tlbl = regalloc_dry_run ? 0 : newiTempLabel(NULL); if (!regalloc_dry_run) { - emit2("ld %s,!immedbyte", use_b ? "b" : "a", shCount); + emit2("ld %s, !immedbyte", use_b ? "b" : "a", shCount); emitLabel(tlbl); } regalloc_dry_run_cost += 2; @@ -7243,7 +8789,7 @@ static void shiftR2Left2Result(const iCode *ic, operand *left, int offl, emit2("djnz !tlabel", labelKey2num(tlbl->key)); else { emit2("dec a"); - emit2("jp NZ,!tlabel", labelKey2num(tlbl->key)); + emit2("jp NZ, !tlabel", labelKey2num(tlbl->key)); } } regalloc_dry_run_cost += use_b ? 2 : 4; @@ -7253,94 +8799,165 @@ static void shiftR2Left2Result(const iCode *ic, operand *left, int offl, /*-----------------------------------------------------------------*/ /* shiftL2Left2Result - shift left two bytes from left to result */ /*-----------------------------------------------------------------*/ -static void shiftL2Left2Result(operand *left, int offl, operand *result, - int offr, int shCount, const iCode *ic) { - operand *shiftoperand = result; - - if (sameRegs(AOP(result), AOP(left)) && ((offl + MSB16) == offr)) { - wassert(0); - } - - if (AOP_TYPE(result) != AOP_REG && AOP_TYPE(left) == AOP_REG && - AOP_SIZE(left) >= 2 && - !bitVectBitValue(ic->rSurv, AOP(left)->aopu.aop_reg[0]->rIdx) && - !bitVectBitValue(ic->rSurv, AOP(left)->aopu.aop_reg[1]->rIdx) || - getPairId(AOP(left)) == PAIR_HL && isPairDead(PAIR_HL, ic)) - shiftoperand = left; - else if (isPair(AOP(result)) && !offr) - fetchPairLong(getPairId(AOP(result)), AOP(left), ic, offl); - else { - /* Copy left into result */ - movLeft2Result(left, offl, result, offr, 0); - movLeft2Result(left, offl + 1, result, offr + 1, 0); +static void shiftL2Left2Result(operand *left, operand *result, int shCount, + const iCode *ic) { + asmop *shiftaop = result->aop; + + /* For a shift of 7 we can use cheaper right shifts */ + if (shCount == 7 && AOP_TYPE(left) == AOP_REG && + !bitVectBitValue(ic->rSurv, AOP(left)->aopu.aop_reg[0]->rIdx) && + AOP_TYPE(result) == AOP_REG && + AOP(left)->aopu.aop_reg[0]->rIdx != IYL_IDX && + AOP(left)->aopu.aop_reg[1]->rIdx != IYL_IDX && + AOP(left)->aopu.aop_reg[0]->rIdx != IYH_IDX && + AOP(left)->aopu.aop_reg[1]->rIdx != IYH_IDX && + AOP(result)->aopu.aop_reg[0]->rIdx != IYL_IDX && + AOP(result)->aopu.aop_reg[1]->rIdx != IYL_IDX && + AOP(result)->aopu.aop_reg[0]->rIdx != IYH_IDX && + AOP(result)->aopu.aop_reg[1]->rIdx != IYH_IDX && + (optimize.codeSpeed || getPairId(AOP(result)) != PAIR_HL || + getPairId(AOP(left)) != PAIR_HL)) /* but a sequence of add hl, hl might + still be cheaper code-size wise */ + { + // Handling the low byte in A with xor clearing is cheaper. + bool special_a = + (!bitVectBitValue(ic->rSurv, A_IDX) && !aopInReg(AOP(left), 0, A_IDX) && + !aopInReg(AOP(left), 0, A_IDX)); + asmop *lowbyte = special_a ? ASMOP_A : AOP(result); + + if (special_a) + emit3(A_XOR, ASMOP_A, ASMOP_A); + emit3_o(A_RR, AOP(left), 1, 0, 0); + emit3_o(A_LD, AOP(result), 1, AOP(left), 0); + emit3_o(A_RR, AOP(result), 1, 0, 0); + if (!special_a) + emit3_o(A_LD, AOP(result), 0, ASMOP_ZERO, 0); + if (aopInReg(lowbyte, 0, A_IDX)) + emit3(A_RRA, 0, 0); + else + emit3(A_RR, lowbyte, 0); + if (special_a) + cheapMove(result->aop, 0, lowbyte, 0, true); + return; } + if ((result->aop->type == AOP_HL || result->aop->type == AOP_IY || + IS_RAB && + result->aop->type == AOP_STK) && // Being able to use cheap add hl, + // hl is worth it in most cases. + (left->aop->type == AOP_HL || left->aop->type == AOP_IY || + IS_RAB && left->aop->type == AOP_STK) && + !IS_GB && isPairDead(PAIR_HL, ic) && + (shCount > 1 || !sameRegs(result->aop, left->aop)) || + isPairDead(PAIR_HL, ic) && !IS_GB && getPairId(result->aop) == PAIR_DE && + getPairId(left->aop) != + PAIR_DE) // Shift in hl if we can cheaply move to de via ex later. + { + shiftaop = ASMOP_HL; + genMove(ASMOP_HL, left->aop, !bitVectBitValue(ic->rSurv, A_IDX), true, + isPairDead(PAIR_DE, ic)); + } else if (AOP_TYPE(result) != AOP_REG && AOP_TYPE(left) == AOP_REG && + AOP_SIZE(left) >= 2 && + !bitVectBitValue(ic->rSurv, + AOP(left)->aopu.aop_reg[0]->rIdx) && + !bitVectBitValue(ic->rSurv, + AOP(left)->aopu.aop_reg[1]->rIdx) || + getPairId(AOP(left)) == PAIR_HL && isPairDead(PAIR_HL, ic)) + shiftaop = left->aop; + else if (isPair(AOP(result))) + fetchPairLong(getPairId(AOP(result)), AOP(left), ic, 0); + else + genMove_o(result->aop, 0, left->aop, 0, 2, + !bitVectBitValue(ic->rSurv, A_IDX), isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); if (shCount == 0) ; - else if (getPairId(AOP(shiftoperand)) == PAIR_HL) { + else if (getPairId(shiftaop) == PAIR_HL) { while (shCount--) { emit2("add hl, hl"); regalloc_dry_run_cost += 1; } - } else if (getPairId(AOP(shiftoperand)) == PAIR_IY) { + } else if (getPairId(shiftaop) == PAIR_IY) { while (shCount--) { emit2("add iy, iy"); regalloc_dry_run_cost += 2; } - } else if (IS_RAB && getPairId(AOP(shiftoperand)) == PAIR_DE) { + } else if (IS_RAB && getPairId(shiftaop) == PAIR_DE && + shCount <= 2 + optimize.codeSpeed) { while (shCount--) { emit3(A_CP, ASMOP_A, ASMOP_A); emit2("rl de"); regalloc_dry_run_cost++; } + } else if (!IS_GB && getPairId(shiftaop) == PAIR_DE) { + emit2("ex de, hl"); + regalloc_dry_run_cost++; + while (shCount--) { + emit2("add hl, hl"); + regalloc_dry_run_cost++; + } + emit2("ex de, hl"); + regalloc_dry_run_cost++; } else { int size = 2; int offset = 0; + + bool use_b = (!IS_GB && !bitVectBitValue(ic->rSurv, B_IDX) && + (shiftaop->type != AOP_REG || + shiftaop->aopu.aop_reg[0]->rIdx != B_IDX && + shiftaop->aopu.aop_reg[1]->rIdx != B_IDX)); + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel(0); - symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel(0); - if (AOP(shiftoperand)->type == AOP_REG) { + if (shiftaop->type == AOP_REG) { while (shCount--) { for (offset = 0; offset < size; offset++) - emit3_o(offset ? A_RL : A_SLA, AOP(shiftoperand), offset, 0, 0); + if (aopInReg(shiftaop, offset, A_IDX)) + emit3(offset ? A_ADC : A_ADD, ASMOP_A, ASMOP_A); + else + emit3_o(offset ? A_RL : A_SLA, shiftaop, offset, 0, 0); } } else { /* Left is already in result - so now do the shift */ if (shCount > 1) { if (!regalloc_dry_run) { - emit2("ld a,!immedbyte+1", shCount); - emit2("jp !tlabel", labelKey2num(tlbl1->key)); + emit2("ld %s, !immedbyte", use_b ? "b" : "a", shCount); emitLabel(tlbl); } - regalloc_dry_run_cost += 4; + regalloc_dry_run_cost += 2; } + if (!use_b && bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + while (size--) { - emit3_o(offset ? A_RL : A_SLA, AOP(shiftoperand), offset, 0, 0); + emit3_o(offset ? A_RL : A_SLA, shiftaop, offset, 0, 0); offset++; } if (shCount > 1) { if (!regalloc_dry_run) { - emitLabel(tlbl1); - emit2("dec a"); - emit2("jp NZ,!tlabel", labelKey2num(tlbl->key)); + if (use_b) + emit2("djnz !tlabel", labelKey2num(tlbl->key)); + else { + emit2("dec a"); + emit2("jp NZ, !tlabel", labelKey2num(tlbl->key)); + } } - regalloc_dry_run_cost += 4; + regalloc_dry_run_cost += use_b ? 2 : 3; } + if (!use_b && bitVectBitValue(ic->rSurv, A_IDX)) + _pop(PAIR_AF); } } - if (shiftoperand != result) { - if (isPair(AOP(result)) && !offr) - fetchPairLong(getPairId(AOP(result)), AOP(shiftoperand), ic, offl); - else if (isPair(AOP(shiftoperand))) - commitPair(AOP(result), getPairId(AOP(shiftoperand)), ic, FALSE); - else { - /* Copy left into result */ - movLeft2Result(shiftoperand, offl, result, offr, 0); - movLeft2Result(shiftoperand, offl + 1, result, offr + 1, 0); - } + if (shiftaop != result->aop) { + if (isPair(AOP(result))) + fetchPairLong(getPairId(AOP(result)), shiftaop, ic, 0); + else + genMove_o(result->aop, 0, shiftaop, 0, 2, + !bitVectBitValue(ic->rSurv, A_IDX), isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); } } @@ -7352,13 +8969,13 @@ static void AccRol(int shCount) { switch (shCount) { case 4: - if (IS_GB) { + if (IS_GB || IS_Z80N) { emit3(A_SWAP, ASMOP_A, 0); break; } emit3(A_RLCA, 0, 0); case 3: - if (IS_GB) { + if (IS_GB || IS_Z80N) { emit3(A_SWAP, ASMOP_A, 0); emit3(A_RRCA, 0, 0); break; @@ -7371,7 +8988,7 @@ static void AccRol(int shCount) { case 0: break; case 5: - if (IS_GB) { + if (IS_GB || IS_Z80N) { emit3(A_SWAP, ASMOP_A, 0); emit3(A_RLCA, 0, 0); break; @@ -7392,19 +9009,15 @@ static void AccLsh(unsigned int shCount) { static const unsigned char SLMask[] = {0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00}; - if (shCount != 0) { - if (shCount == 1) { - emit3(A_ADD, ASMOP_A, ASMOP_A); - } else if (shCount == 2) { - emit3(A_ADD, ASMOP_A, ASMOP_A); + if (shCount <= 3 + !IS_GB) + while (shCount--) emit3(A_ADD, ASMOP_A, ASMOP_A); - } else { - /* rotate left accumulator */ - AccRol(shCount); - /* and kill the lower order bits */ - emit2("and a,!immedbyte", SLMask[shCount]); - regalloc_dry_run_cost += 2; - } + else { + /* rotate left accumulator */ + AccRol(shCount); + /* and kill the lower order bits */ + emit2("and a, !immedbyte", SLMask[shCount]); + regalloc_dry_run_cost += 2; } } @@ -7412,19 +9025,55 @@ static void AccLsh(unsigned int shCount) { /* shiftL1Left2Result - shift left one byte from left to result */ /*-----------------------------------------------------------------*/ static void shiftL1Left2Result(operand *left, int offl, operand *result, - int offr, unsigned int shCount) { + int offr, unsigned int shCount, + const iCode *ic) { + // add hl, hl is cheap in code size. + if (sameRegs(result->aop, left->aop) && aopInReg(result->aop, offr, L_IDX) && + isPairDead(PAIR_HL, ic) && !optimize.codeSpeed && offr == offl) { + while (shCount--) { + emit2("add hl, hl"); + regalloc_dry_run_cost++; + } + } /* If operand and result are the same we can shift in place. However shifting in acc using add is cheaper than shifting in place using sla; when shifting by more than 2 shifting in - acc is worth the additional effort for loading from/to acc. */ - if (sameRegs(AOP(left), AOP(result)) && shCount <= 2 && offr == offl) { + acc it is worth the additional effort for loading from / to acc. */ + else if (!aopInReg(result->aop, 0, A_IDX) && + sameRegs(AOP(left), AOP(result)) && shCount <= 2 && offr == offl) { while (shCount--) emit3(A_SLA, AOP(result), 0); + } else if ((IS_Z180 && !optimize.codeSpeed || IS_EZ80_Z80 || + IS_Z80N) && // Try to use mlt + (!IS_Z80N && aopInReg(result->aop, offr, C_IDX) && + isPairDead(PAIR_BC, ic) || + aopInReg(result->aop, offr, E_IDX) && isPairDead(PAIR_DE, ic) || + !IS_Z80N && aopInReg(result->aop, offr, L_IDX) && + isPairDead(PAIR_HL, ic))) { + PAIR_ID pair = + aopInReg(result->aop, offr, C_IDX) + ? PAIR_BC + : (aopInReg(result->aop, offr, E_IDX) ? PAIR_DE : PAIR_HL); + + bool top = aopInReg(left->aop, offl, _pairs[pair].h_idx); + if (!top) + cheapMove(pair == PAIR_BC ? ASMOP_C + : (pair == PAIR_DE ? ASMOP_E : ASMOP_L), + 0, left->aop, offl, !bitVectBitValue(ic->rSurv, A_IDX)); + + emit2("ld %s, !immed%d", top ? _pairs[pair].l : _pairs[pair].h, + 1 << shCount); + emit2("mlt %s", _pairs[pair].name); + regalloc_dry_run_cost += 4; } else { - cheapMove(ASMOP_A, 0, AOP(left), offl); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _push(PAIR_AF); + cheapMove(ASMOP_A, 0, left->aop, offl, true); /* shift left accumulator */ AccLsh(shCount); - cheapMove(AOP(result), offr, ASMOP_A, 0); + cheapMove(AOP(result), offr, ASMOP_A, 0, true); + if (bitVectBitValue(ic->rSurv, A_IDX)) + _pop(PAIR_AF); } } @@ -7441,34 +9090,23 @@ static void genlshTwo(operand *result, operand *left, unsigned int shCount, if (shCount >= 8) { shCount -= 8; if (size > 1) { - if (shCount) { - movLeft2Result(left, LSB, result, MSB16, 0); - shiftL1Left2Result(left, LSB, result, MSB16, shCount); - aopPut3(AOP(result), LSB, ASMOP_ZERO, 0); - } else { + if (shCount) + shiftL1Left2Result(left, 0, result, 1, shCount, ic); + else movLeft2Result(left, LSB, result, MSB16, 0); - aopPut3(AOP(result), 0, ASMOP_ZERO, 0); - } - } else - aopPut3(AOP(result), LSB, ASMOP_ZERO, 0); + } + cheapMove(result->aop, 0, ASMOP_ZERO, 0, true); } /* 0 <= shCount <= 7 */ else { if (size == 1) { wassert(0); } else { - shiftL2Left2Result(left, LSB, result, LSB, shCount, ic); + shiftL2Left2Result(left, result, shCount, ic); } } } -/*-----------------------------------------------------------------*/ -/* genlshOne - left shift a one byte quantity by known count */ -/*-----------------------------------------------------------------*/ -static void genlshOne(operand *result, operand *left, unsigned int shCount) { - shiftL1Left2Result(left, LSB, result, LSB, shCount); -} - /*------------------------------------------------------------------*/ /* genLeftShiftLiteral - left shifting by known count for size <= 2 */ /*------------------------------------------------------------------*/ @@ -7487,13 +9125,16 @@ static void genLeftShiftLiteral(operand *left, operand *right, operand *result, /* I suppose that the left size >= result size */ wassert(getSize(operandType(left)) >= size); - if (shCount >= (size * 8)) { - while (size--) - aopPut3(AOP(result), size, ASMOP_ZERO, 0); - } else { + if (shCount >= (size * 8)) + genMove(result->aop, ASMOP_ZERO, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic) && left->aop->regs[H_IDX] < 0 && + left->aop->regs[L_IDX] < 0, + isPairDead(PAIR_DE, ic) && left->aop->regs[D_IDX] < 0 && + left->aop->regs[E_IDX] < 0); + else { switch (size) { case 1: - genlshOne(result, left, shCount); + shiftL1Left2Result(left, 0, result, 0, shCount, ic); break; case 2: genlshTwo(result, left, shCount, ic); @@ -7517,7 +9158,10 @@ static void genLeftShift(const iCode *ic) { symbol *tlbl = 0, *tlbl1 = 0; operand *left, *right, *result; int countreg; - bool shift_by_lit, shift_by_one, shift_by_zero; + bool shift_by_lit; + int shiftcount = 0; + int byteshift = 0; + bool started; right = IC_RIGHT(ic); left = IC_LEFT(ic); @@ -7535,8 +9179,8 @@ static void genLeftShift(const iCode *ic) { /* Useful for the case of shifting a size > 2 value by a literal */ shift_by_lit = AOP_TYPE(right) == AOP_LIT; - shift_by_one = (shift_by_lit && ulFromVal(AOP(right)->aopu.aop_lit) == 1); - shift_by_zero = (shift_by_lit && ulFromVal(AOP(right)->aopu.aop_lit) == 0); + if (shift_by_lit) + shiftcount = ulFromVal(AOP(right)->aopu.aop_lit); aopOp(result, ic, FALSE, FALSE); aopOp(left, ic, FALSE, FALSE); @@ -7548,63 +9192,73 @@ static void genLeftShift(const iCode *ic) { (AOP_TYPE(result) != AOP_REG || AOP(result)->aopu.aop_reg[0]->rIdx != AOP(right)->aopu.aop_reg[0]->rIdx && - (AOP_SIZE(result) < 2 || - AOP(result)->aopu.aop_reg[1]->rIdx != - AOP(right)->aopu.aop_reg[0]->rIdx && - (AOP_SIZE(result) < 3 || - AOP(result)->aopu.aop_reg[2]->rIdx != - AOP(right)->aopu.aop_reg[0]->rIdx && - (AOP_SIZE(result) < 4 || - AOP(result)->aopu.aop_reg[3]->rIdx != - AOP(right)->aopu.aop_reg[0]->rIdx))))) + !aopInReg(AOP(result), 0, AOP(right)->aopu.aop_reg[0]->rIdx) && + !aopInReg(AOP(result), 1, AOP(right)->aopu.aop_reg[0]->rIdx) && + !aopInReg(AOP(result), 2, AOP(right)->aopu.aop_reg[0]->rIdx) && + !aopInReg(AOP(result), 3, AOP(right)->aopu.aop_reg[0]->rIdx))) countreg = AOP(right)->aopu.aop_reg[0]->rIdx; else if (!IS_GB && !bitVectBitValue(ic->rSurv, B_IDX) && - (sameRegs(AOP(left), AOP(result)) || AOP_TYPE(left) != AOP_REG) && - (AOP_TYPE(result) != AOP_REG || - AOP(result)->aopu.aop_reg[0]->rIdx != B_IDX && - (AOP_SIZE(result) < 2 || - AOP(result)->aopu.aop_reg[1]->rIdx != B_IDX && - (AOP_SIZE(result) < 3 || - AOP(result)->aopu.aop_reg[2]->rIdx != B_IDX && - (AOP_SIZE(result) < 4 || - AOP(result)->aopu.aop_reg[3]->rIdx != B_IDX))))) + (sameRegs(AOP(left), AOP(result)) || AOP_TYPE(left) != AOP_REG || + shift_by_lit) && + !aopInReg(AOP(result), 0, B_IDX) && + !aopInReg(AOP(result), 1, B_IDX) && + !aopInReg(AOP(result), 2, B_IDX) && !aopInReg(AOP(result), 3, B_IDX)) countreg = B_IDX; - else + else if (result->aop->regs[A_IDX] < 0) + countreg = A_IDX; + else if (!bitVectBitValue(ic->rSurv, B_IDX) && result->aop->regs[B_IDX] < 0 && + left->aop->regs[B_IDX] < 0) + countreg = B_IDX; + else if (!bitVectBitValue(ic->rSurv, C_IDX) && result->aop->regs[C_IDX] < 0 && + left->aop->regs[C_IDX] < 0) + countreg = C_IDX; + else { + wassert(regalloc_dry_run); + regalloc_dry_run_cost += 100; countreg = A_IDX; + } - if (!shift_by_lit) - cheapMove(countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, AOP(right), - 0); + bool save_a_outer = (bitVectBitValue(ic->rSurv, A_IDX) && countreg == A_IDX && + !(shift_by_lit && shiftcount == 1)); - if (AOP_TYPE(left) != AOP_REG || AOP_TYPE(result) != AOP_REG) + if (save_a_outer) _push(PAIR_AF); + if (!shift_by_lit) + cheapMove(asmopregs[countreg], 0, right->aop, 0, true); + + bool save_a_inner = + (countreg == A_IDX && !shift_by_lit) && + !(AOP_TYPE(left) == AOP_REG && AOP_TYPE(result) != AOP_REG || + !IS_GB && (AOP_TYPE(left) == AOP_STK && canAssignToPtr3(result->aop) || + AOP_TYPE(result) == AOP_STK && canAssignToPtr3(left->aop))); + /* now move the left to the result if they are not the same */ if (!sameRegs(AOP(left), AOP(result))) { - int lsize = AOP_SIZE(left); - size = AOP_SIZE(result); - offset = 0; - if (AOP_TYPE(left) == AOP_REG && AOP_TYPE(result) == AOP_REG) { - short src[8], dst[8]; - while (size && lsize) { - src[offset] = AOP(left)->aopu.aop_reg[offset]->rIdx; - dst[offset] = AOP(result)->aopu.aop_reg[offset]->rIdx; - offset++; - size--, lsize--; - } - regMove(dst, src, - AOP_SIZE(result) <= AOP_SIZE(left) ? AOP_SIZE(result) - : AOP_SIZE(left), - TRUE); - while (lsize--) - cheapMove(AOP(result), offset++, ASMOP_ZERO, 0); - } else { - while (size--) { - cheapMove(AOP(result), offset, AOP(left), offset); - offset++; - } + if (save_a_inner) + _push(PAIR_AF); + + if (shift_by_lit) { + byteshift = shiftcount / 8; + shiftcount %= 8; } + size = AOP_SIZE(result) - byteshift; + int lsize = AOP_SIZE(left) - byteshift; + + genMove_o(result->aop, byteshift, left->aop, 0, + size <= lsize ? size : lsize, + (save_a_inner || countreg != A_IDX) && + (!bitVectBitValue(ic->rSurv, A_IDX) || save_a_outer), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); + + genMove_o(result->aop, 0, ASMOP_ZERO, 0, byteshift, + (save_a_inner || countreg != A_IDX) && + (!bitVectBitValue(ic->rSurv, A_IDX) || save_a_outer), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); + + if (save_a_inner) + _pop(PAIR_AF); } if (!regalloc_dry_run) { @@ -7614,46 +9268,66 @@ static void genLeftShift(const iCode *ic) { size = AOP_SIZE(result); offset = 0; - if (AOP_TYPE(left) != AOP_REG || AOP_TYPE(result) != AOP_REG) - _pop(PAIR_AF); - - if (shift_by_zero) + if (shift_by_lit && !shiftcount) goto end; - if (shift_by_lit) - cheapMove(countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, AOP(right), - 0); - else { + if (shift_by_lit && shiftcount > 1) { + emit2("ld %s, !immedbyte", countreg == A_IDX ? "a" : regsZ80[countreg].name, + shiftcount); + regalloc_dry_run_cost += 2; + } else if (!shift_by_lit) { emit2("inc %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); regalloc_dry_run_cost += 1; if (!regalloc_dry_run) emit2("jp !tlabel", labelKey2num(tlbl1->key)); regalloc_dry_run_cost += 3; } - if (!shift_by_one && !regalloc_dry_run) + if (!(shift_by_lit && shiftcount == 1) && !regalloc_dry_run) emitLabel(tlbl); if (requiresHL(AOP(result))) spillPair(PAIR_HL); + started = false; while (size) { - if (size >= 2 && AOP_TYPE(result) == AOP_REG && - (getPartPairId(AOP(result), offset) == PAIR_HL || - IS_RAB && getPartPairId(AOP(result), offset) == PAIR_DE)) { - if (!offset && AOP(result)->aopu.aop_reg[offset]->rIdx == E_IDX) - emit3(A_CP, ASMOP_A, ASMOP_A); - if (AOP(result)->aopu.aop_reg[offset]->rIdx == L_IDX) - emit2(offset ? "adc hl, hl" : "add hl, hl"); - else + if (size >= 2 && offset + 1 >= byteshift && result->aop->type == AOP_REG && + (getPartPairId(result->aop, offset) == PAIR_HL || + !started && getPartPairId(result->aop, offset) == PAIR_IY || + (IS_RAB || optimize.codeSize && !started && !IS_GB) && + getPartPairId(result->aop, offset) == PAIR_DE)) { + if (result->aop->aopu.aop_reg[offset]->rIdx == L_IDX) { + emit2(started ? "adc hl, hl" : "add hl, hl"); + regalloc_dry_run_cost += 1 + started; + } else if (result->aop->aopu.aop_reg[offset]->rIdx == IYL_IDX) { + emit2("add iy, iy"); + regalloc_dry_run_cost += 2; + } else if (IS_RAB) { + if (!started) + emit3(A_CP, ASMOP_A, ASMOP_A); emit2("rl de"); - regalloc_dry_run_cost++; + regalloc_dry_run_cost++; + } else { + wassert(!IS_GB); + emit2("ex de, hl"); + emit2(started ? "adc hl, hl" : "add hl, hl"); + emit2("ex de, hl"); + regalloc_dry_run_cost += 3 + started; + } + + started = true; size -= 2, offset += 2; } else { - emit3_o(offset ? A_RL : A_SLA, AOP(result), offset, 0, 0); + if (offset >= byteshift) { + if (aopInReg(result->aop, offset, A_IDX)) + emit3(started ? A_ADC : A_ADD, ASMOP_A, ASMOP_A); + else + emit3_o(started ? A_RL : A_SLA, result->aop, offset, 0, 0); + started = true; + } size--, offset++; } } - if (!shift_by_one) { + if (!(shift_by_lit && shiftcount == 1)) { if (!regalloc_dry_run) emitLabel(tlbl1); if (!IS_GB && countreg == B_IDX) { @@ -7661,7 +9335,7 @@ static void genLeftShift(const iCode *ic) { emit2("djnz !tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 2; } else { - emit2("dec %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); + emit2("dec %s", regsZ80[countreg].name); if (!regalloc_dry_run) emit2("jr NZ,!tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 3; @@ -7669,6 +9343,13 @@ static void genLeftShift(const iCode *ic) { } end: + if (!shift_by_lit && + requiresHL(AOP(result))) // Shift by 0 skips over hl adjustments. + spillPair(PAIR_HL); + + if (save_a_outer) + _pop(PAIR_AF); + freeAsmop(left, NULL); freeAsmop(right, NULL); freeAsmop(result, NULL); @@ -7683,7 +9364,7 @@ static void AccRsh(int shCount) { AccRol(8 - shCount); /* and kill the higher order bits */ if (!regalloc_dry_run) - emit2("and a,!immedbyte", 0xff >> shCount); + emit2("and a, !immedbyte", 0xff >> shCount); regalloc_dry_run_cost += 2; } else if (shCount) emit3(A_SRL, ASMOP_A, 0); @@ -7693,41 +9374,61 @@ static void AccRsh(int shCount) { /* genrshOne - right shift one byte by known amount */ /*-----------------------------------------------------------------*/ static void genrshOne(operand *result, operand *left, int shCount, - int is_signed) { + int is_signed, const iCode *ic) { /* Errk */ int size = AOP_SIZE(result); wassert(size == 1); - // Shifting in the accumulator is cheap for unsigned operands. - if (!is_signed && - (AOP(result)->type == AOP_ACC || AOP(result)->type != AOP_REG || - (shCount >= 4 && !IS_GB || - AOP(left)->type == - AOP_ACC) /*&& !bitVectBitValue (ic->rSurv, A_IDX)*/)) { - cheapMove(ASMOP_A, 0, AOP(left), 0); + bool a_dead = !bitVectBitValue(ic->rSurv, A_IDX); + + if ((IS_Z180 || IS_EZ80_Z80 || IS_Z80N) && !is_signed && shCount >= 3 && + shCount <= 6 + a_dead && // Try to use mlt. + (!IS_Z80N && aopInReg(result->aop, 0, B_IDX) && isPairDead(PAIR_BC, ic) || + aopInReg(result->aop, 0, D_IDX) && isPairDead(PAIR_DE, ic) || + !IS_Z80N && aopInReg(result->aop, 0, H_IDX) && + isPairDead(PAIR_HL, ic))) { + PAIR_ID pair = aopInReg(result->aop, 0, B_IDX) + ? PAIR_BC + : (aopInReg(result->aop, 0, D_IDX) ? PAIR_DE : PAIR_HL); + bool top = aopInReg(left->aop, 0, _pairs[pair].h_idx); + if (!top) + cheapMove(pair == PAIR_BC ? ASMOP_C + : (pair == PAIR_DE ? ASMOP_E : ASMOP_L), + 0, left->aop, 0, a_dead); + + emit2("ld %s, !immed%d", top ? _pairs[pair].l : _pairs[pair].h, + 1 << (8 - shCount)); + emit2("mlt %s", _pairs[pair].name); + regalloc_dry_run_cost += 4; + } else if (!is_signed && // Shifting in the accumulator is cheap for unsigned + // operands. + (aopInReg(result->aop, 0, A_IDX) || result->aop->type != AOP_REG || + (shCount >= 4 + 2 * a_dead || + shCount >= 2 * a_dead && aopInReg(left->aop, 0, A_IDX)))) { + if (!a_dead) + _push(PAIR_AF); + cheapMove(ASMOP_A, 0, left->aop, 0, true); AccRsh(shCount); - cheapMove(AOP(result), 0, ASMOP_A, 0); + cheapMove(result->aop, 0, ASMOP_A, 0, true); + if (!a_dead) + _pop(PAIR_AF); } else if (AOP(result)->type == AOP_REG) // Can shift in destination for register result. { - cheapMove(AOP(result), 0, AOP(left), 0); - if (!is_signed && IS_GB && shCount >= 3) { - if (shCount == 3) { - emit3(A_RLC, AOP(result), 0); - shCount++; - } - emit3(A_SWAP, AOP(result), 0); - regalloc_dry_run_cost += 2; - shCount -= 4; - } + cheapMove(AOP(result), 0, AOP(left), 0, a_dead); + while (shCount--) - emit3(is_signed ? A_SRA : A_SRL, AOP(result), 0); + emit3(is_signed ? A_SRA : A_SRL, result->aop, 0); } else { - cheapMove(ASMOP_A, 0, AOP(left), 0); + if (!a_dead) + _push(PAIR_AF); + cheapMove(ASMOP_A, 0, left->aop, 0, true); while (shCount--) emit3(is_signed ? A_SRA : A_SRL, ASMOP_A, 0); - cheapMove(AOP(result), 0, ASMOP_A, 0); + cheapMove(result->aop, 0, ASMOP_A, 0, true); + if (!a_dead) + _pop(PAIR_AF); } } @@ -7736,13 +9437,13 @@ static void genrshOne(operand *result, operand *left, int shCount, /*-----------------------------------------------------------------*/ static void shiftR1Left2Result(operand *left, int offl, operand *result, int offr, int shCount, int sign) { - cheapMove(ASMOP_A, 0, AOP(left), offl); + cheapMove(ASMOP_A, 0, left->aop, offl, true); if (sign) { while (shCount--) emit3(sign ? A_SRA : A_SRL, ASMOP_A, 0); } else AccRsh(shCount); - cheapMove(AOP(result), offr, ASMOP_A, 0); + cheapMove(result->aop, offr, ASMOP_A, 0, true); } /*-----------------------------------------------------------------*/ @@ -7760,12 +9461,12 @@ static void genrshTwo(const iCode *ic, operand *result, operand *left, } if (sign) { /* Sign extend the result */ - cheapMove(ASMOP_A, 0, AOP(result), 0); - emit3(A_RLC, ASMOP_A, 0); + cheapMove(ASMOP_A, 0, result->aop, 0, true); + emit3(A_RLCA, 0, 0); emit3(A_SBC, ASMOP_A, ASMOP_A); - cheapMove(AOP(result), MSB16, ASMOP_A, 0); + cheapMove(result->aop, 1, ASMOP_A, 0, true); } else - aopPut3(AOP(result), 1, ASMOP_ZERO, 0); + cheapMove(result->aop, 1, ASMOP_ZERO, 0, true); } /* 0 <= shCount <= 7 */ else @@ -7791,21 +9492,19 @@ static void genRightShiftLiteral(operand *left, operand *right, operand *result, wassert(getSize(operandType(left)) >= size); if (shCount >= (size * 8)) { - asmop *s; if (!SPEC_USIGN(getSpec(operandType(left)))) { - cheapMove(ASMOP_A, 0, AOP(left), 0); - emit3(A_RLC, ASMOP_A, 0); + cheapMove(ASMOP_A, 0, left->aop, 0, true); + emit3(A_RLCA, 0, 0); emit3(A_SBC, ASMOP_A, ASMOP_A); - s = ASMOP_A; - } else { - s = ASMOP_ZERO; - } - while (size--) - cheapMove(AOP(result), size, s, 0); + while (size--) + cheapMove(result->aop, size, ASMOP_A, 0, true); + } else + genMove(result->aop, ASMOP_ZERO, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), false); } else { switch (size) { case 1: - genrshOne(result, left, shCount, sign); + genrshOne(result, left, shCount, sign, ic); break; case 2: genrshTwo(ic, result, left, shCount, sign); @@ -7832,6 +9531,9 @@ static void genRightShift(const iCode *ic) { bool is_signed; int countreg; bool shift_by_lit, shift_by_one, shift_by_zero; + int shiftcount = 0; + int byteoffset = 0; + bool save_a; symbol *tlbl = 0, *tlbl1 = 0; @@ -7857,8 +9559,8 @@ static void genRightShift(const iCode *ic) { /* Useful for the case of shifting a size > 2 value by a literal */ shift_by_lit = AOP_TYPE(right) == AOP_LIT; - shift_by_one = (shift_by_lit && ulFromVal(AOP(right)->aopu.aop_lit) == 1); - shift_by_zero = (shift_by_lit && ulFromVal(AOP(right)->aopu.aop_lit) == 0); + if (shift_by_lit) + shiftcount = ulFromVal(AOP(right)->aopu.aop_lit); aopOp(result, ic, FALSE, FALSE); aopOp(left, ic, FALSE, FALSE); @@ -7881,7 +9583,8 @@ static void genRightShift(const iCode *ic) { AOP(right)->aopu.aop_reg[0]->rIdx))))) countreg = AOP(right)->aopu.aop_reg[0]->rIdx; else if (!IS_GB && !bitVectBitValue(ic->rSurv, B_IDX) && - (sameRegs(AOP(left), AOP(result)) || AOP_TYPE(left) != AOP_REG) && + (sameRegs(AOP(left), AOP(result)) || AOP_TYPE(left) != AOP_REG || + shift_by_lit) && (AOP_TYPE(result) != AOP_REG || AOP(result)->aopu.aop_reg[0]->rIdx != B_IDX && (AOP_SIZE(result) < 2 || @@ -7895,33 +9598,44 @@ static void genRightShift(const iCode *ic) { countreg = A_IDX; if (!shift_by_lit) - cheapMove(countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, AOP(right), - 0); + cheapMove(countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, right->aop, + 0, true); - if (AOP_TYPE(left) != AOP_REG || AOP_TYPE(result) != AOP_REG) - _push(PAIR_AF); + save_a = + (countreg == A_IDX && !shift_by_lit) && + !(AOP_TYPE(left) == AOP_REG && AOP_TYPE(result) != AOP_REG || + !IS_GB && (AOP_TYPE(left) == AOP_STK && canAssignToPtr3(result->aop) || + AOP_TYPE(result) == AOP_STK && canAssignToPtr3(left->aop))); /* now move the left to the result if they are not the same */ if (!sameRegs(AOP(left), AOP(result))) { + int soffset = 0; size = AOP_SIZE(result); - offset = 0; - if (AOP_TYPE(left) == AOP_REG && AOP_TYPE(result) == AOP_REG) { - short src[8], dst[8]; - while (size--) { - src[offset] = AOP(left)->aopu.aop_reg[offset]->rIdx; - dst[offset] = AOP(result)->aopu.aop_reg[offset]->rIdx; - offset++; - } - regMove(dst, src, AOP_SIZE(result), TRUE); - } else { - while (size--) { - cheapMove(AOP(result), offset, AOP(left), offset); - offset++; - } + + if (!is_signed && shift_by_lit) { + byteoffset = shiftcount / 8; + shiftcount %= 8; + soffset = byteoffset; + size -= byteoffset; } + + if (save_a) + _push(PAIR_AF); + + genMove_o(result->aop, 0, left->aop, soffset, size, true, + isPairDead(PAIR_HL, ic), false); + + genMove_o(result->aop, result->aop->size - byteoffset, ASMOP_ZERO, 0, + byteoffset, true, false, false); + + if (save_a) + _pop(PAIR_AF); } + shift_by_one = (shift_by_lit && shiftcount == 1); + shift_by_zero = (shift_by_lit && shiftcount == 0); + if (!regalloc_dry_run) { tlbl = newiTempLabel(NULL); tlbl1 = newiTempLabel(NULL); @@ -7929,15 +9643,13 @@ static void genRightShift(const iCode *ic) { size = AOP_SIZE(result); offset = size - 1; - if (AOP_TYPE(left) != AOP_REG || AOP_TYPE(result) != AOP_REG) - _pop(PAIR_AF); - if (shift_by_zero) goto end; - else if (shift_by_lit) - cheapMove(countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, AOP(right), - 0); - else { + else if (shift_by_lit && shiftcount > 1) { + emit2("ld %s, !immedbyte", countreg == A_IDX ? "a" : regsZ80[countreg].name, + shiftcount); + regalloc_dry_run_cost += 2; + } else if (!shift_by_lit) { emit2("inc %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); regalloc_dry_run_cost += 1; if (!regalloc_dry_run) @@ -7951,7 +9663,7 @@ static void genRightShift(const iCode *ic) { spillPair(PAIR_HL); while (size) { - if (IS_RAB && !(is_signed && first) && size >= 2 && + if (IS_RAB && !(is_signed && first) && size >= 2 && byteoffset < 2 && AOP_TYPE(result) == AOP_REG && (getPartPairId(AOP(result), offset - 1) == PAIR_HL || getPartPairId(AOP(result), offset - 1) == PAIR_DE)) { @@ -7963,7 +9675,9 @@ static void genRightShift(const iCode *ic) { : "rr de"); regalloc_dry_run_cost++; size -= 2, offset -= 2; - } else if (first) { + } else if (!is_signed && first && byteoffset--) // Skip known 0 bytes + size--, offset--; + else if (first) { emit3_o(is_signed ? A_SRA : A_SRL, AOP(result), offset, 0, 0); first = 0; size--, offset--; @@ -7983,27 +9697,58 @@ static void genRightShift(const iCode *ic) { } else { emit2("dec %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); if (!regalloc_dry_run) - emit2("jr NZ,!tlabel", labelKey2num(tlbl->key)); + emit2("jr NZ, !tlabel", labelKey2num(tlbl->key)); regalloc_dry_run_cost += 3; } } end: + if (!shift_by_lit && + requiresHL(AOP(result))) // Shift by 0 skips over hl adjustments. + spillPair(PAIR_HL); + freeAsmop(left, NULL); freeAsmop(right, NULL); freeAsmop(result, NULL); } +/*-----------------------------------------------------------------*/ +/* unpackMaskA - generate masking code for unpacking last byte */ +/* of bitfiled. And mask for unsigned, sign extension for signed. */ +/*-----------------------------------------------------------------*/ +static void unpackMaskA(sym_link *type, int len) { + if (SPEC_USIGN(type) || len != 1) { + emit2("and a, !immedbyte", ((unsigned char)-1) >> (8 - len)); + regalloc_dry_run_cost += 2; + } + if (!SPEC_USIGN(type)) { + if (len == 1) { + emit3(A_RRA, 0, 0); + emit3(A_SBC, ASMOP_A, ASMOP_A); + } else { + if (!regalloc_dry_run) { + symbol *tlbl = newiTempLabel(NULL); + emit2("bit %d, a", len - 1); + emit2("jp Z, !tlabel", labelKey2num(tlbl->key)); + emit2("or a, !immedbyte", (unsigned char)(0xff << len)); + emitLabel(tlbl); + } + regalloc_dry_run_cost += 7; + } + } +} + /*-----------------------------------------------------------------*/ /* genUnpackBits - generates code for unpacking bits */ /*-----------------------------------------------------------------*/ -static void genUnpackBits(operand *result, int pair) { +static void genUnpackBits(operand *result, int pair, const iCode *ic) { int offset = 0; /* result byte offset */ int rsize; /* result size */ int rlen = 0; /* remaining bit-field length */ sym_link *etype; /* bit-field type information */ unsigned blen; /* bit-field length */ unsigned bstr; /* bit-field starting bit within byte */ + unsigned int pairincrement = 0; emitDebug("; genUnpackBits"); @@ -8014,23 +9759,11 @@ static void genUnpackBits(operand *result, int pair) { /* If the bit-field length is less than a byte */ if (blen < 8) { - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; AccRol(8 - bstr); - emit2("and a,!immedbyte", ((unsigned char)-1) >> (8 - blen)); - regalloc_dry_run_cost += 2; - if (!SPEC_USIGN(etype)) { - /* signed bit-field */ - if (!regalloc_dry_run) { - symbol *tlbl = newiTempLabel(NULL); - emit2("bit %d,a", blen - 1); - emit2("jp Z,!tlabel", labelKey2num(tlbl->key)); - emit2("or a,!immedbyte", (unsigned char)(0xff << blen)); - emitLabel(tlbl); - } - regalloc_dry_run_cost += 7; - } - cheapMove(AOP(result), offset++, ASMOP_A, 0); + unpackMaskA(etype, blen); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); goto finish; } @@ -8040,32 +9773,21 @@ static void genUnpackBits(operand *result, int pair) { (AOP(result)->aopu.aop_reg[0]->rIdx == L_IDX || AOP(result)->aopu.aop_reg[0]->rIdx == H_IDX)) { wassertl(rsize == 2, "HL must be of size 2"); - emit2("ld a,!*hl"); + emit2("ld a, !*hl"); emit2("inc hl"); if (AOP_TYPE(result) != AOP_REG || AOP(result)->aopu.aop_reg[0]->rIdx != H_IDX) { - emit2("ld h,!*hl"); - cheapMove(AOP(result), offset++, ASMOP_A, 0); - emit2("ld a,h"); + emit2("ld h, !*hl"); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); + emit2("ld a, h"); } else { - emit2("ld l,!*hl"); - cheapMove(AOP(result), offset++, ASMOP_A, 0); - emit2("ld a,l"); - } - emit2("and a,!immedbyte", ((unsigned char)-1) >> (16 - blen)); - regalloc_dry_run_cost += 7; - if (!SPEC_USIGN(etype)) { - /* signed bit-field */ - if (!regalloc_dry_run) { - symbol *tlbl = newiTempLabel(NULL); - emit2("bit %d,a", blen - 1 - 8); - emit2("jp Z,!tlabel", labelKey2num(tlbl->key)); - emit2("or a,!immedbyte", (unsigned char)(0xff << (blen - 8))); - emitLabel(tlbl); - } - regalloc_dry_run_cost += 7; + emit2("ld l, !*hl"); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); + emit2("ld a, l"); } - cheapMove(AOP(result), offset++, ASMOP_A, 0); + regalloc_dry_run_cost += 5; + unpackMaskA(etype, blen - 8); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); regalloc_dry_run_cost += 1; spillPair(PAIR_HL); return; @@ -8074,36 +9796,34 @@ static void genUnpackBits(operand *result, int pair) { /* Bit field did not fit in a byte. Copy all but the partial byte at the end. */ for (rlen = blen; rlen >= 8; rlen -= 8) { - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += 1; - cheapMove(AOP(result), offset++, ASMOP_A, 0); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); if (rlen > 8) { emit2("inc %s", _pairs[pair].name); regalloc_dry_run_cost += 1; _G.pairs[pair].offset++; + pairincrement++; } } /* Handle the partial byte at the end */ if (rlen) { - emit2("ld a,!*pair", _pairs[pair].name); - emit2("and a,!immedbyte", ((unsigned char)-1) >> (8 - rlen)); - regalloc_dry_run_cost += 3; - if (!SPEC_USIGN(etype)) { - /* signed bit-field */ - if (!regalloc_dry_run) { - symbol *tlbl = newiTempLabel(NULL); - emit2("bit %d,a", rlen - 1); - emit2("jp Z,!tlabel", labelKey2num(tlbl->key)); - emit2("or a,!immedbyte", (unsigned char)(0xff << rlen)); - emitLabel(tlbl); - } - regalloc_dry_run_cost += 7; - } - cheapMove(AOP(result), offset++, ASMOP_A, 0); + emit2("ld a, !mems", _pairs[pair].name); + regalloc_dry_run_cost++; + unpackMaskA(etype, rlen); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); } finish: + if (!isPairDead(pair, ic)) + while (pairincrement) { + emit2("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + pairincrement--; + _G.pairs[pair].offset--; + } + if (offset < rsize) { asmop *source; @@ -8117,19 +9837,20 @@ static void genUnpackBits(operand *result, int pair) { } rsize -= offset; while (rsize--) - cheapMove(AOP(result), offset++, source, 0); + cheapMove(AOP(result), offset++, source, 0, true); } } static void _moveFrom_tpair_(asmop *aop, int offset, PAIR_ID pair) { - if (!IS_GB && aop->type == AOP_REG) { + emitDebug("; _moveFrom_tpair_()"); + if (pair == PAIR_HL && aop->type == AOP_REG) { if (!regalloc_dry_run) aopPut(aop, "!*hl", offset); regalloc_dry_run_cost += ld_cost(aop, ASMOP_A); } else { - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += 1; - cheapMove(aop, offset, ASMOP_A, 0); + cheapMove(aop, offset, ASMOP_A, 0, true); } } @@ -8187,12 +9908,16 @@ static void genPointerGet(const iCode *ic) { if (IS_GB) wassert(!rightval); - if (IS_GB || IY_RESERVED && requiresHL(AOP(result)) && size > 1 && - AOP_TYPE(result) != AOP_REG) + if (IS_GB && + left->aop->type == AOP_STK) // Try to avoid (hl) to hl copy, which + // requires 3 instructions and free a. + pair = PAIR_DE; + if ((IS_GB || IY_RESERVED) && requiresHL(result->aop) && size > 1 && + result->aop->type != AOP_REG) pair = PAIR_DE; - if (AOP_TYPE(left) == AOP_IMMD && size == 1 && AOP_TYPE(result) == AOP_ACC && - !IS_BITVAR(retype)) { + if (AOP_TYPE(left) == AOP_IMMD && size == 1 && + aopInReg(result->aop, 0, A_IDX) && !IS_BITVAR(retype)) { emit2("ld a, (%s)", aopGetLitWordLong(AOP(left), rightval, TRUE)); regalloc_dry_run_cost += 3; goto release; @@ -8218,15 +9943,16 @@ static void genPointerGet(const iCode *ic) { goto release; } - if (isPair(AOP(left)) && size == 1 && !IS_BITVAR(retype) && !rightval) { + if (isPair(left->aop) && size == 1 && !IS_BITVAR(retype) && !rightval) { /* Just do it */ - if (isPtrPair(AOP(left))) { + if ((getPairId(AOP(left)) == PAIR_HL || getPairId(AOP(left)) == PAIR_IY) && + result->aop->type == AOP_REG) { if (!regalloc_dry_run) // Todo: More exact cost. { struct dbuf_s dbuf; dbuf_init(&dbuf, 128); - dbuf_tprintf(&dbuf, "!*pair", getPairName(AOP(left))); + dbuf_tprintf(&dbuf, "!mems", getPairName(AOP(left))); aopPut(AOP(result), dbuf_c_str(&dbuf), 0); dbuf_destroy(&dbuf); } @@ -8234,9 +9960,10 @@ static void genPointerGet(const iCode *ic) { } else { if (surviving_a && !pushed_a) _push(PAIR_AF), pushed_a = TRUE; - emit2("ld a,!*pair", getPairName(AOP(left))); + emit2("ld a, !mems", getPairName(AOP(left))); regalloc_dry_run_cost += (getPairId(AOP(left)) == PAIR_IY ? 3 : 1); - cheapMove(AOP(result), 0, ASMOP_A, 0); + genMove(result->aop, ASMOP_A, true, isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); } goto release; @@ -8244,14 +9971,28 @@ static void genPointerGet(const iCode *ic) { if (getPairId(AOP(left)) == PAIR_IY && !IS_BITVAR(retype) && rightval_in_range) { - if ((IS_RAB || IS_TLCS90) && getPairId(AOP(result)) == PAIR_HL) { - emit2("ld hl, (iy + %d)", rightval); + offset = 0; + + if ((IS_RAB || IS_TLCS90) && getPartPairId(AOP(result), 0) == PAIR_HL) { + emit2("ld hl, %d (iy)", rightval); regalloc_dry_run_cost += 3; - goto release; + offset = 2; + size -= 2; + } else if (IS_EZ80_Z80 && getPartPairId(AOP(result), 0) != PAIR_INVALID) { + emit2("ld %s, %d (iy)", _pairs[getPartPairId(AOP(result), 0)].name, + rightval); + regalloc_dry_run_cost += 3; + offset = 2; + size -= 2; } + if (!size) + goto release; + /* Just do it */ - offset = 0; + if (surviving_a && !pushed_a) + _push(PAIR_AF), pushed_a = TRUE; + while (size--) { if (!regalloc_dry_run) { struct dbuf_s dbuf; @@ -8270,9 +10011,9 @@ static void genPointerGet(const iCode *ic) { } /* Using ldir is cheapest for large memory-to-memory transfers. */ - if (!IS_GB && + if (!IS_GB && !IS_R2K && !IS_R2KA && (AOP_TYPE(result) == AOP_STK || AOP_TYPE(result) == AOP_EXSTK) && - size > 2 && (!rightval || AOP_TYPE(left) == AOP_IMMD)) { + size > 2) { int fp_offset, sp_offset; if (!isPairDead(PAIR_HL, ic)) @@ -8282,22 +10023,51 @@ static void genPointerGet(const iCode *ic) { if (!isPairDead(PAIR_BC, ic)) _push(PAIR_BC); - if (!rightval) - fetchPair(PAIR_DE, AOP(left)); - else { - emit2("ld de, %s", aopGetLitWordLong(AOP(left), rightval, TRUE)); + fp_offset = AOP(result)->aopu.aop_stk + + (AOP(result)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (IS_EZ80_Z80 && !_G.omitFramePtr && fp_offset >= -128 && + fp_offset < 128) { + if (AOP_TYPE(left) == AOP_IMMD) { + emit2("ld hl, %s", aopGetLitWordLong(AOP(left), rightval, TRUE)); + regalloc_dry_run_cost += 3; + } else + fetchPair(PAIR_HL, AOP(left)); + emit2("lea de, ix, !immed%d", fp_offset); regalloc_dry_run_cost += 3; + } else { + if (AOP_TYPE(left) == AOP_IMMD) { + emit2("ld de, %s", aopGetLitWordLong(AOP(left), rightval, TRUE)); + regalloc_dry_run_cost += 3; + } else + fetchPair(PAIR_DE, AOP(left)); + emit2("ld hl, !immedword", sp_offset); + emit2("add hl, sp"); + emit2("ex de, hl"); + regalloc_dry_run_cost += 5; } - fp_offset = AOP(result)->aopu.aop_stk + _G.stack.offset + - (AOP(result)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - sp_offset = fp_offset + _G.stack.pushed; - emit2("ld hl, !immedword", sp_offset); - emit2("add hl, sp"); - emit2("ex de, hl"); + if (rightval && left->aop->type != AOP_IMMD) + if (abs(rightval) < 4) { + for (; rightval > 0; rightval--) { + emit2("inc hl"); + regalloc_dry_run_cost++; + } + for (; rightval < 0; rightval++) { + emit2("inc hl"); + regalloc_dry_run_cost++; + } + } else { + emit2("ld bc, !immedword", rightval); + emit2("add hl, bc"); + rightval = 0; + regalloc_dry_run_cost += 4; + } + emit2("ld bc, !immedword", size); emit2("ldir"); - regalloc_dry_run_cost += 10; + regalloc_dry_run_cost += 5; spillPair(PAIR_HL); spillPair(PAIR_DE); spillPair(PAIR_BC); @@ -8314,6 +10084,13 @@ static void genPointerGet(const iCode *ic) { extrapair = isPairDead(PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + if (!surviving_a && + (getPairId(left->aop) == PAIR_BC || getPairId(left->aop) == PAIR_DE) && + isPairDead(getPairId(left->aop), ic) && abs(rightval) <= 2 && + !IS_BITVAR(retype) && size < 2) // Use inc ss (size < 2 condition to avoid + // overwriting pair with result) + pair = getPairId(left->aop); + /* For now we always load into temp pair */ /* if this is rematerializable */ if (!IS_GB && @@ -8324,35 +10101,64 @@ static void genPointerGet(const iCode *ic) { SPEC_BLEN(getSpec(operandType(result))) < 8 && rightval_in_range) pair = getPairId(AOP(left)); else { - if (!isPairDead(pair, ic) && size > 1) + if (!isPairDead(pair, ic) && size > 1 && + (getPairId(AOP(left)) != pair || rightval || IS_BITVAR(retype) || + size > 2)) // For simple cases, restoring via dec is cheaper than push + // / pop. _push(pair), pushed_pair = TRUE; if (AOP_TYPE(left) == AOP_IMMD) { emit2("ld %s, %s", _pairs[pair].name, aopGetLitWordLong(AOP(left), rightval, TRUE)); + spillPair(pair); regalloc_dry_run_cost += 3; rightval = 0; + } else if (pair == PAIR_HL && rightval > 2 && + (getPairId(left->aop) == PAIR_BC || + getPairId(left->aop) == + PAIR_DE)) // Cheaper than moving to hl followed by offset + // adjustment. + { + emit2("ld hl, #%d", rightval); + emit2("add hl, %s", _pairs[getPairId(left->aop)].name); + regalloc_dry_run_cost += 4; + rightval = 0; } else - fetchPair(pair, AOP(left)); + fetchPair(pair, left->aop); } /* if bit then unpack */ if (IS_BITVAR(retype)) { offsetPair(pair, extrapair, !isPairDead(extrapair, ic), rightval); - genUnpackBits(result, pair); + genUnpackBits(result, pair, ic); if (rightval) spillPair(pair); goto release; } - if (getPairId(AOP(result)) == PAIR_HL || - size == 2 && AOP_TYPE(result) == AOP_REG && - (AOP(result)->aopu.aop_reg[0] == regsZ80 + L_IDX || - AOP(result)->aopu.aop_reg[0] == regsZ80 + H_IDX)) { + if (isPair(AOP(result)) && IS_EZ80_Z80 && getPairId(AOP(left)) == PAIR_HL && + !IS_BITVAR(retype) && !rightval) { + emit2("ld %s, (hl)", _pairs[getPairId(AOP(result))].name); + regalloc_dry_run_cost += 2; + goto release; + } else if (pair == PAIR_HL && + (getPairId(AOP(result)) == PAIR_HL || + size == 2 && (aopInReg(result->aop, 0, L_IDX) || + aopInReg(result->aop, 0, H_IDX)))) { wassertl(size == 2, "HL must be of size 2"); if (IS_RAB && getPairId(AOP(result)) == PAIR_HL && rightval_in_range) { emit2("ld hl, %d (hl)", rightval); regalloc_dry_run_cost += 3; + } else if (IS_EZ80_Z80 && getPairId(AOP(result)) == PAIR_HL && !rightval) { + emit2("ld hl, (hl)"); + regalloc_dry_run_cost += 2; + } else if (aopInReg(result->aop, 1, A_IDX)) { + offsetPair(pair, extrapair, !isPairDead(extrapair, ic), rightval + 1); + emit2("ld a, !*hl"); + emit2("dec hl"); + if (!regalloc_dry_run) + aopPut(AOP(result), "!*hl", 0); + regalloc_dry_run_cost += 3; } else { if (surviving_a && !pushed_a) _push(PAIR_AF), pushed_a = TRUE; @@ -8362,7 +10168,7 @@ static void genPointerGet(const iCode *ic) { if (!regalloc_dry_run) aopPut(AOP(result), "!*hl", 1); regalloc_dry_run_cost += 3; - cheapMove(AOP(result), 0, ASMOP_A, 0); + cheapMove(result->aop, 0, ASMOP_A, 0, true); } spillPair(PAIR_HL); goto release; @@ -8376,6 +10182,7 @@ static void genPointerGet(const iCode *ic) { AOP_TYPE(result) == AOP_STK)) { size = AOP_SIZE(result); offset = 0; + int last_offset = 0; /* might use ld a,(hl) followed by ld d (iy),a */ if ((AOP_TYPE(result) == AOP_EXSTK || AOP_TYPE(result) == AOP_STK) && @@ -8397,11 +10204,11 @@ static void genPointerGet(const iCode *ic) { { r = (l == -10 ? h : l); - while (size--) { + while (offset < size) { if (offset != r) _moveFrom_tpair_(AOP(result), offset, pair); - if (size) { + if (offset < size) { offset++; emit2("inc %s", _pairs[pair].name); regalloc_dry_run_cost += 1; @@ -8422,12 +10229,28 @@ static void genPointerGet(const iCode *ic) { } else if (l >= 0 && h >= 0) // Two bytes of result somewehere in hl. // Assign it last and use a for caching. { - while (size--) { + while (offset < size) { + last_offset = offset; + + if (IS_EZ80_Z80 && offset != l && offset != h && + getPairId_o(result->aop, offset) != PAIR_INVALID) { + emit2("ld %s, !*hl", _pairs[getPairId_o(result->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + if (offset < size) { + emit2("inc %s", _pairs[pair].name); + emit2("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + _G.pairs[pair].offset += 2; + } + continue; + } + if (offset != l && offset != h) _moveFrom_tpair_(AOP(result), offset, pair); + offset++; - if (size) { - offset++; + if (offset < size) { emit2("inc %s", _pairs[pair].name); regalloc_dry_run_cost += 1; _G.pairs[pair].offset++; @@ -8435,11 +10258,13 @@ static void genPointerGet(const iCode *ic) { } r = (l > h ? l : h); - for (size = offset; size != r; size--) { + for (size = last_offset; size != r; size--) { emit2("dec %s", _pairs[pair].name); regalloc_dry_run_cost += 1; } - if (surviving_a && !pushed_a) + if ((surviving_a || + result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < r) && + !pushed_a) _push(PAIR_AF), pushed_a = TRUE; _moveFrom_tpair_(ASMOP_A, 0, pair); @@ -8451,7 +10276,7 @@ static void genPointerGet(const iCode *ic) { _moveFrom_tpair_(AOP(result), r, pair); r = (l > h ? l : h); - cheapMove(AOP(result), r, ASMOP_A, 0); + cheapMove(result->aop, r, ASMOP_A, 0, true); // No fixup since result uses HL. spillPair(pair); @@ -8459,10 +10284,25 @@ static void genPointerGet(const iCode *ic) { } } - while (size--) { + while (offset < size) { + last_offset = offset; + + if (IS_EZ80_Z80 && getPairId_o(result->aop, offset) != PAIR_INVALID) { + emit2("ld %s, !*hl", _pairs[getPairId_o(result->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + if (offset < size) { + emit2("inc %s", _pairs[pair].name); + emit2("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + _G.pairs[pair].offset += 2; + } + continue; + } + _moveFrom_tpair_(AOP(result), offset++, pair); - if (size) { + if (offset < size) { emit2("inc %s", _pairs[pair].name); regalloc_dry_run_cost += 1; _G.pairs[pair].offset++; @@ -8470,7 +10310,7 @@ static void genPointerGet(const iCode *ic) { } /* Fixup HL back down */ if (getPairId(AOP(left)) == pair && !isPairDead(pair, ic) && !pushed_pair) - for (size = AOP_SIZE(result) - 1; size; size--) { + while (last_offset-- > 0) { emit2("dec %s", _pairs[pair].name); regalloc_dry_run_cost += 1; _G.pairs[pair].offset--; @@ -8481,28 +10321,33 @@ static void genPointerGet(const iCode *ic) { size = AOP_SIZE(result); offset = 0; - while (size--) { + for (offset = 0; offset < size;) { if (surviving_a && !pushed_a) _push(PAIR_AF), pushed_a = TRUE; /* PENDING: make this better */ - if (!IS_GB && - (AOP_TYPE(result) == AOP_REG || AOP_TYPE(result) == AOP_HLREG)) { + if ((pair == PAIR_HL) && AOP_TYPE(result) == AOP_REG) { if (!regalloc_dry_run) aopPut(AOP(result), "!*hl", offset++); regalloc_dry_run_cost += ld_cost(AOP(result), ASMOP_A); } else { - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += 1; - cheapMove(AOP(result), offset++, ASMOP_A, 0); + cheapMove(result->aop, offset++, ASMOP_A, 0, true); } - if (size) { + if (offset < size) { emit2("inc %s", _pairs[pair].name); regalloc_dry_run_cost += 1; _G.pairs[pair].offset++; } } - if (rightval || AOP_SIZE(result)) + if (!isPairDead(pair, ic)) + while (offset-- > 1) { + emit2("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset--; + } + else if (rightval || AOP_SIZE(result)) spillPair(pair); } @@ -8517,10 +10362,9 @@ static void genPointerGet(const iCode *ic) { } static bool isRegOrLit(asmop *aop) { - if (aop->type == AOP_REG || aop->type == AOP_LIT || aop->type == AOP_IMMD || - aop->type == AOP_HLREG) - return TRUE; - return FALSE; + if (aop->type == AOP_REG || aop->type == AOP_LIT || aop->type == AOP_IMMD) + return true; + return false; } /*-----------------------------------------------------------------*/ @@ -8549,31 +10393,51 @@ static void genPackBits(sym_link *etype, operand *right, int pair, mask = ((unsigned char)(0xFF << (blen + bstr)) | (unsigned char)(0xFF >> (8 - bstr))); - if (AOP_TYPE(right) == AOP_LIT) { + if (AOP_TYPE(right) == AOP_LIT && blen == 1 && + (pair == PAIR_HL || pair == PAIR_IX || pair == PAIR_IY)) { + litval = (int)ulFromVal(AOP(right)->aopu.aop_lit); + emit2(litval & 1 ? "set %d, !mems" : "res %d, !mems", bstr, + _pairs[pair].name); + regalloc_dry_run_cost = (pair == PAIR_IX || pair == PAIR_IY) ? 4 : 2; + return; + } else if (AOP_TYPE(right) == AOP_LIT) { /* Case with a bit-field length <8 and literal source */ litval = (int)ulFromVal(AOP(right)->aopu.aop_lit); litval <<= bstr; litval &= (~mask) & 0xff; - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; if ((mask | litval) != 0xff) { - emit2("and a,!immedbyte", mask); + emit2("and a, !immedbyte", mask); regalloc_dry_run_cost += 2; } if (litval) { - emit2("or a,!immedbyte", litval); + emit2("or a, !immedbyte", litval); regalloc_dry_run_cost += 1; } - emit2("ld !*pair,a", _pairs[pair].name); + emit2("ld !mems, a", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; return; + } else if (blen == 4 && bstr % 4 == 0 && pair == PAIR_HL && + !aopInReg(right->aop, 0, A_IDX) && !requiresHL(right->aop) && + (IS_Z80 || IS_Z180 || IS_EZ80_Z80 || IS_Z80N)) { + emit2(bstr ? "rld" : "rrd"); + regalloc_dry_run_cost += 2; + cheapMove(ASMOP_A, 0, AOP(right), 0, true); + emit2(bstr ? "rrd" : "rld"); + regalloc_dry_run_cost += 2; + return; } else { /* Case with a bit-field length <8 and arbitrary source */ - cheapMove(ASMOP_A, 0, AOP(right), 0); + cheapMove(ASMOP_A, 0, AOP(right), 0, true); /* shift and mask source value */ - AccLsh(bstr); - emit2("and a,!immedbyte", (~mask) & 0xff); - regalloc_dry_run_cost += 2; + if (blen + bstr == 8) + AccLsh(bstr); + else { + AccRol(bstr); + emit2("and a, !immedbyte", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + } extraPair = getFreePairId(ic); if (extraPair == PAIR_INVALID) { @@ -8587,17 +10451,17 @@ static void genPackBits(sym_link *etype, operand *right, int pair, } } } - emit2("ld %s,a", _pairs[extraPair].l); + emit2("ld %s, a", _pairs[extraPair].l); spillPair(extraPair); regalloc_dry_run_cost += 1; - emit2("ld a,!*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; - emit2("and a,!immedbyte", mask); + emit2("and a, !immedbyte", mask); regalloc_dry_run_cost += 2; - emit2("or a,%s", _pairs[extraPair].l); + emit2("or a, %s", _pairs[extraPair].l); regalloc_dry_run_cost += 1; - emit2("ld !*pair,a", _pairs[pair].name); + emit2("ld !mems, a", _pairs[pair].name); regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; if (needPopExtra) _pop(extraPair); @@ -8608,12 +10472,12 @@ static void genPackBits(sym_link *etype, operand *right, int pair, /* Bit length is greater than 7 bits. In this case, copy */ /* all except the partial byte at the end */ for (rlen = blen; rlen >= 8; rlen -= 8) { - cheapMove(ASMOP_A, 0, AOP(right), offset++); + cheapMove(ASMOP_A, 0, AOP(right), offset++, true); if (pair == PAIR_IX || pair == PAIR_IY) { - emit2("ld (!*offspair + %d),a", _pairs[pair].name, pair_offset); + emit2("ld %d !mems, a", pair_offset, _pairs[pair].name); regalloc_dry_run_cost += 3; } else { - emit2("ld !*pair,a", _pairs[pair].name); + emit2("ld !mems, a", _pairs[pair].name); regalloc_dry_run_cost += 1; } if (rlen > 8 && pair != PAIR_IX && pair != PAIR_IY) { @@ -8636,21 +10500,21 @@ static void genPackBits(sym_link *etype, operand *right, int pair, litval &= (~mask) & 0xff; if (pair == PAIR_IX || pair == PAIR_IY) { - emit2("ld a, (!*offspair + %d)", _pairs[pair].name, pair_offset); + emit2("ld a, %d !mems", pair_offset, _pairs[pair].name); regalloc_dry_run_cost += 3; } else { - emit2("ld a, !*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += 1; } if ((mask | litval) != 0xff) - emit2("and a,!immedbyte", mask); + emit2("and a, !immedbyte", mask); if (litval) - emit2("or a,!immedbyte", litval); + emit2("or a, !immedbyte", litval); } else { /* Case with partial byte and arbitrary source */ - cheapMove(ASMOP_A, 0, AOP(right), offset++); - emit2("and a,!immedbyte", (~mask) & 0xff); + cheapMove(ASMOP_A, 0, AOP(right), offset++, true); + emit2("and a, !immedbyte", (~mask) & 0xff); regalloc_dry_run_cost += 2; extraPair = getFreePairId(ic); @@ -8671,25 +10535,25 @@ static void genPackBits(sym_link *etype, operand *right, int pair, regalloc_dry_run_cost += 1; if (pair == PAIR_IX || pair == PAIR_IY) { - emit2("ld a, (!*offspair + %d)", _pairs[pair].name, pair_offset); + emit2("ld a, %d !mems", pair_offset, _pairs[pair].name); regalloc_dry_run_cost += 3; } else { - emit2("ld a, !*pair", _pairs[pair].name); + emit2("ld a, !mems", _pairs[pair].name); regalloc_dry_run_cost += 1; } - emit2("and a,!immedbyte", mask); + emit2("and a, !immedbyte", mask); regalloc_dry_run_cost += 2; - emit2("or a,%s", _pairs[extraPair].l); + emit2("or a, %s", _pairs[extraPair].l); regalloc_dry_run_cost += 1; if (needPopExtra) _pop(extraPair); } if (pair == PAIR_IX || pair == PAIR_IY) { - emit2("ld (!*offspair + %d), a", _pairs[pair].name, pair_offset); + emit2("ld %d !mems, a", pair_offset, _pairs[pair].name); regalloc_dry_run_cost += 3; } else { - emit2("ld !*pair, a", _pairs[pair].name); + emit2("ld !mems, a", _pairs[pair].name); regalloc_dry_run_cost += 1; } } @@ -8707,6 +10571,7 @@ static void genPackBits(sym_link *etype, operand *right, int pair, /*-----------------------------------------------------------------*/ static void genPointerSet(iCode *ic) { int size, offset = 0; + int last_offset = 0; operand *right, *result; PAIR_ID pairId = PAIR_HL; bool isBitvar; @@ -8725,6 +10590,9 @@ static void genPointerSet(iCode *ic) { if (IS_GB) pairId = isRegOrLit(AOP(right)) ? PAIR_HL : PAIR_DE; + else if (IY_RESERVED) + pairId = (isRegOrLit(AOP(right)) || AOP_TYPE(right) == AOP_STK) ? PAIR_HL + : PAIR_DE; if (isPair(AOP(result)) && isPairDead(getPairId(AOP(result)), ic)) pairId = getPairId(AOP(result)); @@ -8741,25 +10609,26 @@ static void genPointerSet(iCode *ic) { isPtr(pair)) // Todo: correct cost for pair iy. { if (!regalloc_dry_run) - emit2("ld !*pair,%s", pair, aopGet(AOP(right), 0, FALSE)); + emit2("ld !mems, %s", pair, aopGet(AOP(right), 0, FALSE)); regalloc_dry_run_cost += ld_cost(ASMOP_A, AOP(right)) + (getPairId(AOP(result)) != PAIR_IY ? 0 : 2); } else { - if (surviving_a && !pushed_a && AOP_TYPE(right) != AOP_ACC) + if (surviving_a && !pushed_a && !aopInReg(right->aop, 0, A_IDX)) _push(PAIR_AF), pushed_a = TRUE; if (AOP_TYPE(right) == AOP_LIT && byteOfVal(AOP(right)->aopu.aop_lit, offset) == 0x00) emit3(A_XOR, ASMOP_A, ASMOP_A); else - cheapMove(ASMOP_A, 0, AOP(right), 0); - emit2("ld !*pair,a", pair); + cheapMove(ASMOP_A, 0, AOP(right), 0, true); + emit2("ld !mems, a", pair); regalloc_dry_run_cost += (getPairId(AOP(result)) != PAIR_IY ? 1 : 3); } goto release; } /* Using ldir is cheapest for large memory-to-memory transfers. */ - if (!IS_GB && (AOP_TYPE(right) == AOP_STK || AOP_TYPE(right) == AOP_EXSTK) && + if (!IS_GB && !IS_R2K && !IS_R2KA && + (AOP_TYPE(right) == AOP_STK || AOP_TYPE(right) == AOP_EXSTK) && size > 2) { int fp_offset, sp_offset; @@ -8767,18 +10636,22 @@ static void genPointerSet(iCode *ic) { _push(PAIR_DE); if (!isPairDead(PAIR_BC, ic)) _push(PAIR_BC); + if (!isPairDead(PAIR_HL, ic)) + _push(PAIR_HL); fetchPair(PAIR_DE, AOP(result)); - fp_offset = AOP(right)->aopu.aop_stk + _G.stack.offset + + fp_offset = AOP(right)->aopu.aop_stk + (AOP(right)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - sp_offset = fp_offset + _G.stack.pushed; + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; emit2("ld hl, !immedword", sp_offset); emit2("add hl, sp"); emit2("ld bc, !immedword", size); emit2("ldir"); regalloc_dry_run_cost += 9; + if (!isPairDead(PAIR_HL, ic)) + _pop(PAIR_HL); if (!isPairDead(PAIR_BC, ic)) _pop(PAIR_BC); if (!isPairDead(PAIR_DE, ic)) @@ -8791,41 +10664,61 @@ static void genPointerSet(iCode *ic) { while (size--) { if (canAssignToPtr3(AOP(right))) { if (!regalloc_dry_run) - emit2("ld !*iyx,%s", offset, aopGet(AOP(right), offset, FALSE)); + emit2("ld !*iyx, %s", offset, aopGet(AOP(right), offset, FALSE)); regalloc_dry_run_cost += 3; // Todo: More exact cost here! } else { - cheapMove(ASMOP_A, 0, AOP(right), offset); - emit2("ld !*iyx,a", offset); + cheapMove(ASMOP_A, 0, AOP(right), offset, true); + emit2("ld !*iyx, a", offset); regalloc_dry_run_cost += 3; } offset++; } goto release; - } else if (getPairId(AOP(result)) == PAIR_HL && !isPairDead(PAIR_HL, ic) && + } else if (getPairId(result->aop) == PAIR_HL && !isPairDead(PAIR_HL, ic) && !isBitvar) { - while (size--) { - if (isRegOrLit(AOP(right)) && !IS_GB) { + while (offset < size) { + last_offset = offset; + + if (IS_EZ80_Z80 && offset + 1 < size && + getPairId_o(right->aop, offset) != PAIR_INVALID) { + emit2("ld !mems, %s", _pairs[PAIR_HL].name, + _pairs[getPairId_o(right->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + + if (offset < size) { + emit2("inc %s", _pairs[PAIR_HL].name); + emit2("inc %s", _pairs[PAIR_HL].name); + regalloc_dry_run_cost += 2; + _G.pairs[PAIR_HL].offset++; + } + + continue; + } else if (isRegOrLit(AOP(right)) && !IS_GB) { if (!regalloc_dry_run) - emit2("ld !*pair,%s", _pairs[PAIR_HL].name, - aopGet(AOP(right), offset, FALSE)); - regalloc_dry_run_cost += ld_cost(ASMOP_A, AOP(right)); + emit2("ld !mems, %s", _pairs[PAIR_HL].name, + aopGet(right->aop, offset, FALSE)); + regalloc_dry_run_cost += ld_cost(ASMOP_A, right->aop); + offset++; } else { - if (surviving_a && !pushed_a && (AOP_TYPE(right) != AOP_ACC || offset)) + if (surviving_a && !pushed_a && + (!aopInReg(right->aop, 0, A_IDX) || offset)) _push(PAIR_AF), pushed_a = TRUE; - cheapMove(ASMOP_A, 0, AOP(right), offset); - emit2("ld !*pair,a", _pairs[PAIR_HL].name); + cheapMove(ASMOP_A, 0, right->aop, offset, true); + emit2("ld !mems, a", _pairs[PAIR_HL].name); regalloc_dry_run_cost += 1; + offset++; } - if (size) { + + if (offset < size) { emit2("inc %s", _pairs[PAIR_HL].name); regalloc_dry_run_cost += 1; _G.pairs[PAIR_HL].offset++; } - offset++; } /* Fixup HL back down */ - for (size = AOP_SIZE(right) - 1; size; size--) { + while (last_offset-- > 0) { emit2("dec %s", _pairs[PAIR_HL].name); regalloc_dry_run_cost += 1; } @@ -8838,7 +10731,7 @@ static void genPointerSet(iCode *ic) { isLitWord(AOP(right)))) { if (isLitWord(AOP(right))) { pairId = PAIR_HL; - fetchPair(pairId, AOP(right)); + fetchPairLong(pairId, AOP(right), ic, 0); } else pairId = getPairId(AOP(right)); emit2("ld (%s), %s", aopGetLitWordLong(AOP(result), offset, FALSE), @@ -8864,8 +10757,9 @@ static void genPointerSet(iCode *ic) { fetchPairLong(pairId, AOP(right), ic, 2); } else pairId = getPartPairId(AOP(right), 2); - emit2("ld (%s), %s", aopGetLitWordLong(AOP(result), offset + 2, FALSE), - _pairs[pairId].name); + emit2("ld (%s+%d), %s", aopGetLitWordLong(AOP(result), offset, FALSE), 2, + _pairs[pairId].name); // Handling of literal addresses is somewhat + // broken, use explicit offset as workaround. regalloc_dry_run_cost += (pairId == PAIR_HL) ? 3 : 4; goto release; } @@ -8880,7 +10774,7 @@ static void genPointerSet(iCode *ic) { ic))) /* Avoid destroying result by increments */ pairId = getPairId(AOP(result)); else - fetchPair(pairId, AOP(result)); + fetchPairLong(pairId, AOP(result), ic, 0); } /* so hl now contains the address */ /*freeAsmop (result, NULL, ic);*/ @@ -8890,31 +10784,63 @@ static void genPointerSet(iCode *ic) { genPackBits((IS_BITVAR(retype) ? retype : letype), right, pairId, ic); goto release; } else { - offset = 0; + bool zero_a = false; - while (size--) { - if (isRegOrLit(AOP(right)) && pairId == PAIR_HL) { + for (offset = 0; offset < size;) { + last_offset = offset; + + if (IS_EZ80_Z80 && offset + 1 < size && pairId == PAIR_HL && + getPairId_o(right->aop, offset) != PAIR_INVALID) { + emit2("ld !mems, %s", _pairs[pairId].name, + _pairs[getPairId_o(right->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + + if (offset < size) { + emit2("inc %s", _pairs[pairId].name); + emit2("inc %s", _pairs[pairId].name); + regalloc_dry_run_cost += 2; + _G.pairs[pairId].offset++; + } + + continue; + } + + if (!zero_a && offset + 1 < size && + aopIsLitVal(right->aop, offset, 2, 0x0000) && !surviving_a) { + emit2("xor a, a"); + regalloc_dry_run_cost++; + zero_a = true; + } + + if (aopIsLitVal(right->aop, offset, 1, 0x00) && zero_a) { + emit2("ld !mems, a", _pairs[pairId].name); + regalloc_dry_run_cost++; + } else if (isRegOrLit(right->aop) && pairId == PAIR_HL) { if (!regalloc_dry_run) - emit2("ld !*pair,%s", _pairs[pairId].name, + emit2("ld !mems, %s", _pairs[pairId].name, aopGet(AOP(right), offset, FALSE)); regalloc_dry_run_cost += ld_cost(ASMOP_A, AOP(right)); } else { - if (surviving_a && !pushed_a && (AOP_TYPE(right) != AOP_ACC || offset)) + if (surviving_a && !pushed_a && + (!aopInReg(right->aop, 0, A_IDX) || offset)) _push(PAIR_AF), pushed_a = TRUE; - cheapMove(ASMOP_A, 0, AOP(right), offset); - emit2("ld !*pair,a", _pairs[pairId].name); + cheapMove(ASMOP_A, 0, AOP(right), offset, true); + zero_a = false; + emit2("ld !mems, a", _pairs[pairId].name); regalloc_dry_run_cost += 1; } - if (size) { + offset++; + + if (offset < size) { emit2("inc %s", _pairs[pairId].name); regalloc_dry_run_cost += 1; _G.pairs[pairId].offset++; } - offset++; } /* Restore operand partially in HL. */ - if (!isPairDead(pairId, ic)) { - while (offset-- > 1) { + if (!isPairDead(pairId, ic) && AOP(result)->type == AOP_REG) { + while (last_offset-- > 0) { emit2("dec %s", _pairs[pairId].name); regalloc_dry_run_cost += 1; _G.pairs[pairId].offset--; @@ -8939,23 +10865,39 @@ static void genIfx(iCode *ic, iCode *popIc) { aopOp(cond, ic, FALSE, TRUE); - /* get the value into acc */ - if (AOP_TYPE(cond) != AOP_CRY && !IS_BOOL(operandType(cond))) - _toBoolean(cond, FALSE); /* Special case: Condition is bool */ - else if (IS_BOOL(operandType(cond))) { + if (IS_BOOL(operandType(cond)) && !aopInReg(cond->aop, 0, A_IDX)) { if (!regalloc_dry_run) { - emit2("bit 0,%s", aopGet(AOP(cond), 0, FALSE)); - emit2("jp %s,!tlabel", IC_TRUE(ic) ? "NZ" : "Z", - labelKey2num((IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->key)); + emit2("bit 0, %s", aopGet(cond->aop, 0, FALSE)); + genIfxJump(ic, "nz"); } - regalloc_dry_run_cost += (bit8_cost(AOP(cond)) + 3); + regalloc_dry_run_cost += bit8_cost(cond->aop); - freeAsmop(cond, NULL); - if (!regalloc_dry_run) - ic->generated = 1; - return; - } else + goto release; + } else if (cond->aop->size == 1 && bitVectBitValue(ic->rSurv, A_IDX) && + (aopInReg(cond->aop, 0, B_IDX) || aopInReg(cond->aop, 0, C_IDX) || + aopInReg(cond->aop, 0, D_IDX) || aopInReg(cond->aop, 0, E_IDX) || + aopInReg(cond->aop, 0, H_IDX) || aopInReg(cond->aop, 0, L_IDX))) { + emit3(A_INC, cond->aop, 0); + emit3(A_DEC, cond->aop, 0); + regalloc_dry_run_cost += 2; + genIfxJump(ic, "nz"); + + goto release; + } else if (IS_RAB && + (getPairId(cond->aop) == PAIR_HL || + getPairId(cond->aop) == PAIR_IY) && + isPairDead(getPairId(cond->aop), ic)) { + emit2("bool %s", _pairs[getPairId(cond->aop)].name); + regalloc_dry_run_cost += 1 + (getPairId(cond->aop) == PAIR_IY); + genIfxJump(ic, "nz"); + + goto release; + } + /* get the value into acc */ + else if (AOP_TYPE(cond) != AOP_CRY) + _toBoolean(cond, !popIc); + else isbit = 1; /* the result is now in the accumulator */ freeAsmop(cond, NULL); @@ -8970,10 +10912,20 @@ static void genIfx(iCode *ic, iCode *popIc) { else if (isbit && !IS_ITEMP(cond)) genIfxJump(ic, OP_SYMBOL(cond)->rname); else - genIfxJump(ic, "a"); + genIfxJump(ic, popIc ? "a" : "nz"); + + if (!regalloc_dry_run) + ic->generated = 1; + + return; + +release: + freeAsmop(cond, NULL); if (!regalloc_dry_run) ic->generated = 1; + + return; } /*-----------------------------------------------------------------*/ @@ -8981,69 +10933,44 @@ static void genIfx(iCode *ic, iCode *popIc) { /*-----------------------------------------------------------------*/ static void genAddrOf(const iCode *ic) { symbol *sym; + PAIR_ID pair; operand *right = IC_RIGHT(ic); wassert(IS_TRUE_SYMOP(IC_LEFT(ic))); wassert(right && IS_OP_LITERAL(IC_RIGHT(ic))); sym = OP_SYMBOL(IC_LEFT(ic)); - aopOp(IC_RESULT(ic), ic, FALSE, FALSE); - /* if the operand is on the stack then we - need to get the stack offset of this - variable */ - if (IS_GB) { - if (sym->onStack) { - spillPair(PAIR_HL); - if (sym->stack <= 0) { - setupPairFromSP(PAIR_HL, sym->stack + _G.stack.pushed + - _G.stack.offset + - (int)operandLitValue(right)); - } else { - setupPairFromSP(PAIR_HL, sym->stack + _G.stack.pushed + - _G.stack.offset + _G.stack.param_offset + - (int)operandLitValue(right)); - } - commitPair(AOP(IC_RESULT(ic)), PAIR_HL, ic, FALSE); - } else { - emit2("ld de,!hashedstr+%ld", sym->rname, (long)(operandLitValue(right))); + if (sym->onStack) { + int fp_offset = sym->stack + (sym->stack > 0 ? _G.stack.param_offset : 0) + + (int)operandLitValue(right); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + bool in_fp_range = + !_G.omitFramePtr && (fp_offset >= -128 && fp_offset < 128); + + if (IS_EZ80_Z80 && in_fp_range && + getPairId(IC_RESULT(ic)->aop) != PAIR_INVALID) + pair = getPairId(IC_RESULT(ic)->aop); + else + pair = (getPairId(IC_RESULT(ic)->aop) == PAIR_IY) ? PAIR_IY : PAIR_HL; + spillPair(pair); + if (IS_EZ80_Z80 && in_fp_range) { + emit2("lea %s, ix, !immed%d", _pairs[pair].name, fp_offset); regalloc_dry_run_cost += 3; - commitPair(AOP(IC_RESULT(ic)), PAIR_DE, ic, FALSE); - } + } else + setupPairFromSP(pair, sp_offset); } else { - PAIR_ID pair; - - if (sym->onStack) { - if (getPairId(AOP(IC_RESULT(ic))) == PAIR_IY) - pair = PAIR_IY; - else { - pair = PAIR_HL; - spillPair(PAIR_HL); - } - - /* if it has an offset then we need to compute it */ - if (sym->stack > 0) - emit2("ld %s,!immedword", _pairs[pair].name, - (int)(sym->stack + _G.stack.pushed + _G.stack.offset + - _G.stack.param_offset + operandLitValue(right))); - else - emit2("ld %s,!immedword", _pairs[pair].name, - (int)(sym->stack + _G.stack.pushed + _G.stack.offset + - operandLitValue(right))); - regalloc_dry_run_cost += (pair == PAIR_IY ? 4 : 3); - emit2("add %s,sp", _pairs[pair].name); - regalloc_dry_run_cost += (pair == PAIR_IY ? 2 : 1); - } else { - pair = getPairId(AOP(IC_RESULT(ic))); - if (pair == PAIR_INVALID) { - pair = PAIR_HL; - spillPair(PAIR_HL); - } - emit2("ld %s,!hashedstr+%ld", _pairs[pair].name, sym->rname, - (long)(operandLitValue(right))); - regalloc_dry_run_cost += (pair == PAIR_IY ? 4 : 3); + pair = getPairId(IC_RESULT(ic)->aop); + if (pair == PAIR_INVALID) { + pair = IS_GB ? PAIR_DE : PAIR_HL; + spillPair(pair); } - commitPair(AOP(IC_RESULT(ic)), pair, ic, FALSE); + emit2("ld %s, !hashedstr+%ld", _pairs[pair].name, sym->rname, + (long)(operandLitValue(right))); + regalloc_dry_run_cost += (pair == PAIR_IY ? 4 : 3); } + + commitPair(IC_RESULT(ic)->aop, pair, ic, FALSE); + freeAsmop(IC_RESULT(ic), NULL); } @@ -9053,11 +10980,12 @@ static void genAddrOf(const iCode *ic) { static void genAssign(const iCode *ic) { operand *result, *right; int size, offset; - unsigned long lit = 0L; result = IC_RESULT(ic); right = IC_RIGHT(ic); + const bool hl_dead = isPairDead(PAIR_HL, ic); + /* Dont bother assigning if they are the same */ if (operandsEqu(IC_RESULT(ic), IC_RIGHT(ic))) return; @@ -9067,7 +10995,7 @@ static void genAssign(const iCode *ic) { /* if they are the same registers */ if (sameRegs(AOP(right), AOP(result))) { - emitDebug("; (registers are the same)"); + emitDebug("; (locations are the same)"); goto release; } @@ -9080,18 +11008,15 @@ static void genAssign(const iCode *ic) { size = AOP_SIZE(result); offset = 0; - if (AOP_TYPE(right) == AOP_LIT) { - lit = ulFromVal(AOP(right)->aopu.aop_lit); - } - if (isPair(AOP(result))) fetchPairLong(getPairId(AOP(result)), AOP(right), ic, LSB); else if (isPair(AOP(right)) && AOP_TYPE(result) == AOP_IY && size == 2) - commitPair(AOP(result), getPairId(AOP(right)), ic, FALSE); + genMove(result->aop, right->aop, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); else if (size == 2 && isPairDead(PAIR_HL, ic) && (!IS_GB && (AOP_TYPE(right) == AOP_STK && !_G.omitFramePtr || - AOP_TYPE(right) == AOP_IY) && + AOP_TYPE(right) == AOP_IY || AOP_TYPE(right) == AOP_LIT) && AOP_TYPE(result) == AOP_IY || // Use ld (nn), hl !IS_GB && AOP_TYPE(right) == AOP_IY && (AOP_TYPE(result) == AOP_STK && !_G.omitFramePtr || @@ -9109,11 +11034,13 @@ static void genAssign(const iCode *ic) { AOP_TYPE(right) == AOP_IMMD))) // Use ld d(sp), hl { fetchPair(PAIR_HL, AOP(right)); - commitPair(AOP(result), PAIR_HL, ic, FALSE); + genMove(result->aop, ASMOP_HL, !bitVectBitValue(ic->rSurv, A_IDX), true, + isPairDead(PAIR_DE, ic)); } else if (size == 2 && getPairId(AOP(right)) != PAIR_INVALID && - getPairId(AOP(right)) != PAIR_IY && AOP_TYPE(result) != AOP_REG) { - commitPair(AOP(result), getPairId(AOP(right)), ic, TRUE); - } else if (getPairId(AOP(right)) == PAIR_IY) { + getPairId(AOP(right)) != PAIR_IY && AOP_TYPE(result) != AOP_REG) + genMove(result->aop, right->aop, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); + else if (getPairId(AOP(right)) == PAIR_IY) { while (size--) { if (size == 0) { if (IS_TLCS90) { @@ -9134,7 +11061,7 @@ static void genAssign(const iCode *ic) { emit2("ld (%s+%d), a", AOP(result)->aopu.aop_dir, size); regalloc_dry_run_cost += 3; } else - cheapMove(AOP(result), size, ASMOP_A, 0); + cheapMove(AOP(result), size, ASMOP_A, 0, true); } else if (size == 1) { if (AOP_TYPE(result) == AOP_IY) /* Take care not to overwrite iy */ { @@ -9160,59 +11087,42 @@ static void genAssign(const iCode *ic) { emit2("push iy"); emit2("pop af"); regalloc_dry_run_cost += 3; - cheapMove(AOP(result), size, ASMOP_A, 0); + cheapMove(AOP(result), size, ASMOP_A, 0, true); } } else { if (AOP_TYPE(result) == AOP_IY) /* Take care not to overwrite iy */ { - cheapMove(ASMOP_A, 0, ASMOP_ZERO, 0); + cheapMove(ASMOP_A, 0, ASMOP_ZERO, 0, true); emit2("ld (%s+%d), a", AOP(result)->aopu.aop_dir, size); regalloc_dry_run_cost += 3; } else - cheapMove(AOP(result), size, ASMOP_ZERO, 0); - } - } - } else if ((size > 1) && (AOP_TYPE(result) != AOP_REG) && - (AOP_TYPE(right) == AOP_LIT) && !IS_FLOAT(operandType(right)) && - (lit < 256L)) { - bool fXored = FALSE; - offset = 0; - /* Work from the top down. - Done this way so that we can use the cached copy of 0 - in A for a fast clear */ - while (size--) { - if ((unsigned int)((lit >> (offset * 8)) & 0x0FFL) == 0) { - if (!fXored && size > 1) { - emit3(A_XOR, ASMOP_A, ASMOP_A); - fXored = TRUE; - } - cheapMove(AOP(result), offset, fXored ? ASMOP_A : ASMOP_ZERO, 0); - } else - cheapMove(AOP(result), offset, AOP(right), offset); - offset++; + cheapMove(AOP(result), size, ASMOP_ZERO, 0, true); + } } } else if (size == 2 && requiresHL(AOP(right)) && requiresHL(AOP(result)) && - isPairDead(PAIR_DE, ic) && (IS_GB /*|| IY_RESERVED */)) { + isPairDead(PAIR_DE, ic) && IS_GB) { /* Special case. Load into a and d, then load out. */ - cheapMove(ASMOP_A, 0, AOP(right), 0); + cheapMove(ASMOP_A, 0, AOP(right), 0, true); emit3_o(A_LD, ASMOP_E, 0, AOP(right), 1); - cheapMove(AOP(result), 0, ASMOP_A, 0); - cheapMove(AOP(result), 1, ASMOP_E, 0); - } else if (size == 4 && requiresHL(AOP(right)) && requiresHL(AOP(result)) && - isPairDead(PAIR_DE, ic) && (IS_GB /*|| IY_RESERVED */)) { + cheapMove(AOP(result), 0, ASMOP_A, 0, true); + cheapMove(AOP(result), 1, ASMOP_E, 0, true); + } else if (size == 4 && + (requiresHL(right->aop) && right->aop->type != AOP_REG) && + (requiresHL(result->aop) && result->aop->type != AOP_REG) && + isPairDead(PAIR_DE, ic) && (IS_GB || IY_RESERVED)) { /* Special case - simple memcpy */ if (!regalloc_dry_run) { aopGet(AOP(right), LSB, FALSE); - emit2("ld d,h"); - emit2("ld e,l"); + emit2("ld d, h"); + emit2("ld e, l"); aopGet(AOP(result), LSB, FALSE); } regalloc_dry_run_cost += 8; // Todo: More exact cost here! while (size--) { - emit2("ld a,(de)"); + emit2("ld a, (de)"); /* Peephole will optimise this. */ - emit2("ld (hl),a"); + emit2("ld (hl), a"); regalloc_dry_run_cost += 2; if (size != 0) { emit2("inc hl"); @@ -9222,7 +11132,9 @@ static void genAssign(const iCode *ic) { } spillPair(PAIR_HL); } else { - if (!IS_GB && /* gbz80 doesn't have ldir */ + if (!IS_GB && // gbz80 doesn't have ldir, r2k and r2ka ldir is affected by a + // wait state bug when copying between different types of + // memory. (AOP_TYPE(result) == AOP_STK || AOP_TYPE(result) == AOP_EXSTK || AOP_TYPE(result) == AOP_DIR || AOP_TYPE(result) == AOP_IY) && (AOP_TYPE(right) == AOP_STK || AOP_TYPE(right) == AOP_EXSTK || @@ -9240,11 +11152,11 @@ static void genAssign(const iCode *ic) { 13 + hl_alive * 2 + de_alive * 2 + bc_alive * 2 - (AOP_TYPE(right) == AOP_DIR || AOP_TYPE(right) == AOP_IY) - (AOP_TYPE(result) == AOP_DIR || AOP_TYPE(result) == AOP_IY) * 2; - if (IS_Z180) + if (IS_Z180 || IS_EZ80_Z80) cyclecost_n = 30 * size; else /* Z80 */ cyclecost_n = 38 * size; - if (IS_Z180) + if (IS_Z180 || IS_EZ80_Z80) cyclecost_l = 14 * size + 42 + hl_alive * 22 + de_alive * 22 + bc_alive * 22 - (AOP_TYPE(right) == AOP_DIR || AOP_TYPE(right) == AOP_IY) * 7 - @@ -9271,34 +11183,48 @@ static void genAssign(const iCode *ic) { if (AOP_TYPE(result) == AOP_STK || AOP_TYPE(result) == AOP_EXSTK) { int fp_offset = - AOP(result)->aopu.aop_stk + offset + _G.stack.offset + + AOP(result)->aopu.aop_stk + offset + (AOP(result)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - int sp_offset = fp_offset + _G.stack.pushed; - emit2("ld hl, #%d", sp_offset); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2("ld hl, !immed%d", sp_offset); emit2("add hl, sp"); emit2("ex de, hl"); regalloc_dry_run_cost += 5; } else { - emit2("ld de, #%s", AOP(IC_RESULT(ic))->aopu.aop_dir); + emit2("ld de, !hashedstr", AOP(IC_RESULT(ic))->aopu.aop_dir); regalloc_dry_run_cost += 3; } if (AOP_TYPE(right) == AOP_STK || AOP_TYPE(right) == AOP_EXSTK) { int fp_offset = - AOP(right)->aopu.aop_stk + offset + _G.stack.offset + + AOP(right)->aopu.aop_stk + offset + (AOP(right)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); - int sp_offset = fp_offset + _G.stack.pushed; - emit2("ld hl, #%d", sp_offset); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2("ld hl, !immed%d", sp_offset); emit2("add hl, sp"); regalloc_dry_run_cost += 4; } else { - emit2("ld hl, #%s", AOP(IC_RIGHT(ic))->aopu.aop_dir); + emit2("ld hl, !hashedstr", AOP(IC_RIGHT(ic))->aopu.aop_dir); regalloc_dry_run_cost += 3; } + spillPair(PAIR_HL); - emit2("ld bc, #%d", size); - emit2("ldir"); - regalloc_dry_run_cost += 5; + if (size <= 2 + optimize.codeSpeed || // Early Rabbits have a wait state + // bug when ldir copies between + // different types of memory. + (IS_R2K || IS_R2KA) && !((right->aop->type == AOP_STK || + right->aop->type == AOP_EXSTK) && + (result->aop->type == AOP_STK || + result->aop->type == AOP_EXSTK))) + for (int i = 0; i < size; i++) { + emit2("ldi"); + regalloc_dry_run_cost += 2; + } + else { + emit2("ld bc, !immed%d", size); + emit2("ldir"); + regalloc_dry_run_cost += 5; + } if (bc_alive) _pop(PAIR_BC); @@ -9310,84 +11236,33 @@ static void genAssign(const iCode *ic) { goto release; } } - if (AOP_TYPE(result) == AOP_REG && AOP_TYPE(right) == AOP_REG) { - // We need to be able to handle any assignment here, ensuring not to - // overwrite any parts of the source that we still need. - - bool assigned[8] = { - FALSE, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE}; // This has to be made bigger when sdcc supports - // variables larger than 8 bytes in registers. - int cached_byte = -1; - - while (size--) { - int i; - - // Find lowest byte that can be assigned and needs to be assigned. - for (i = 0; i < AOP_SIZE(result); i++) { - int j; - - if (assigned[i]) - continue; - - for (j = 0; - j < (AOP_SIZE(result) < AOP_SIZE(right) ? AOP_SIZE(result) - : AOP_SIZE(right)); - j++) { - if (!assigned[j] && i != j && - AOP(result)->aopu.aop_reg[i]->rIdx == - AOP(right)->aopu.aop_reg[j]->rIdx) - goto skip_byte; // We can't write this one without overwriting the - // source. - } - - break; // Found byte that can be written safely. - - skip_byte:; - } - - if (i < AOP_SIZE(result)) { - cheapMove(AOP(result), i, AOP(right), - i); // We can safely assign a byte. - assigned[i] = TRUE; - continue; - } - - // No byte can be assigned safely (i.e. the assignment is a - // permutation). Cache one in the accumulator. - - if (cached_byte != -1) { - // Already one cached. Can happen when the assignment is a permutation - // consisting of multiple cycles. - cheapMove(AOP(result), cached_byte, ASMOP_A, 0); - cached_byte = -1; - continue; - } - - for (i = 0; i < AOP_SIZE(result); i++) - if (!assigned[i]) - break; - wassertl(i != AOP_SIZE(result), "genAssign error: Trying to cache " - "non-existant byte in accumulator."); - cheapMove(ASMOP_A, 0, AOP(right), i); - assigned[i] = TRUE; - cached_byte = i; - } - - if (cached_byte != -1) - cheapMove(AOP(result), cached_byte, ASMOP_A, 0); - } else + if ((result->aop->type == AOP_REG || result->aop->type == AOP_STK || + result->aop->type == AOP_EXSTK) && + (right->aop->type == AOP_REG || right->aop->type == AOP_STK || + right->aop->type == AOP_EXSTK || right->aop->type == AOP_LIT || + right->aop->type == AOP_IMMD)) + genMove(result->aop, right->aop, !bitVectBitValue(ic->rSurv, A_IDX), + isPairDead(PAIR_HL, ic), isPairDead(PAIR_DE, ic)); + else while (size--) { - /* PENDING: do this check better */ - if ((IS_GB || IY_RESERVED) && requiresHL(AOP(right)) && - requiresHL(AOP(result))) { + const bool hl_free = hl_dead && (right->aop->regs[L_IDX] <= offset) && + (right->aop->regs[H_IDX] <= offset) && + (result->aop->regs[L_IDX] < 0 || + result->aop->regs[L_IDX] >= offset) && + (result->aop->regs[H_IDX] < 0 || + result->aop->regs[H_IDX] >= offset); + const bool save_hl = + !hl_free && ((IS_GB || IY_RESERVED) && + (requiresHL(right->aop) || requiresHL(result->aop))); + + if (save_hl) _push(PAIR_HL); - cheapMove(ASMOP_A, 0, AOP(right), offset); - cheapMove(AOP(result), offset, ASMOP_A, 0); + cheapMove(result->aop, offset, right->aop, offset, + !bitVectBitValue(ic->rSurv, A_IDX)); + if (save_hl) { _pop(PAIR_HL); spillPair(PAIR_HL); - } else - cheapMove(AOP(result), offset, AOP(right), offset); + } offset++; } } @@ -9402,30 +11277,49 @@ static void genAssign(const iCode *ic) { /*-----------------------------------------------------------------*/ static void genJumpTab(const iCode *ic) { symbol *jtab = NULL; + operand *jtcond = IC_JTCOND(ic); + bool pushed_pair = FALSE; + PAIR_ID pair; - aopOp(IC_JTCOND(ic), ic, FALSE, FALSE); - /* get the condition into accumulator */ - if (!IS_GB && !isPairDead(PAIR_DE, ic)) - _push(PAIR_DE); - cheapMove(ASMOP_E, 0, AOP(IC_JTCOND(ic)), 0); + aopOp(jtcond, ic, FALSE, FALSE); + + // Choose extra pair DE or BC for addition + if (AOP_TYPE(jtcond) == AOP_REG && + AOP(jtcond)->aopu.aop_reg[0]->rIdx == E_IDX && isPairDead(PAIR_DE, ic)) + pair = PAIR_DE; + else if (AOP_TYPE(jtcond) == AOP_REG && + AOP(jtcond)->aopu.aop_reg[0]->rIdx == C_IDX && + isPairDead(PAIR_BC, ic)) + pair = PAIR_BC; + else if ((pair = getDeadPairId(ic)) == PAIR_INVALID) + pair = PAIR_DE; + + if (!isPairDead(pair, ic)) { + _push(pair); + pushed_pair = TRUE; + } + + cheapMove(pair == PAIR_DE ? ASMOP_E : ASMOP_C, 0, AOP(jtcond), 0, true); if (!regalloc_dry_run) { - emit2("ld d,!zero"); + emit2("ld %s, !zero", _pairs[pair].h); jtab = newiTempLabel(NULL); } regalloc_dry_run_cost += 2; spillPair(PAIR_HL); if (!regalloc_dry_run) { - emit2("ld hl,!immed!tlabel", labelKey2num(jtab->key)); - emit2("add hl,de"); - emit2("add hl,de"); - emit2("add hl,de"); + emit2("ld hl, !immed!tlabel", labelKey2num(jtab->key)); + emit2("add hl, %s", _pairs[pair].name); + emit2("add hl, %s", _pairs[pair].name); + emit2("add hl, %s", _pairs[pair].name); } regalloc_dry_run_cost += 5; freeAsmop(IC_JTCOND(ic), NULL); - if (!IS_GB && !isPairDead(PAIR_DE, ic)) - _pop(PAIR_DE); + + if (pushed_pair) + _pop(pair); + if (!regalloc_dry_run) { - emit2("jp !*hl"); + emit2("!jphl"); emitLabelSpill(jtab); } regalloc_dry_run_cost += 1; @@ -9461,13 +11355,22 @@ static void genCast(const iCode *ic) { } /* casting to bool */ + if (IS_BOOL(operandType(result)) && IS_RAB && right->aop->size == 2 && + (aopInReg(right->aop, 0, HL_IDX) && isPairDead(PAIR_HL, ic) || + aopInReg(right->aop, 0, IY_IDX) && isPairDead(PAIR_IY, ic))) { + bool iy = aopInReg(right->aop, 0, IY_IDX); + emit2("bool %s", _pairs[getPairId(right->aop)].name); + cheapMove(result->aop, 0, iy ? ASMOP_IYL : ASMOP_L, 0, + !bitVectBitValue(ic->rSurv, A_IDX)); + goto release; + } if (IS_BOOL(operandType(result))) { _castBoolean(right); outAcc(result); goto release; } - /* if they are the same size : or less */ + /* if they are the same size or less */ if (AOP_SIZE(result) <= AOP_SIZE(right)) { genAssign(ic); goto release; @@ -9475,30 +11378,33 @@ static void genCast(const iCode *ic) { /* So we now know that the size of destination is greater than the size of the source */ - /* we move to result for the size of source */ - size = AOP_SIZE(right); - offset = 0; - while (size--) { - cheapMove(AOP(result), offset, AOP(right), offset); - offset++; - } + genMove_o(result->aop, 0, right->aop, 0, right->aop->size - 1, true, + isPairDead(PAIR_HL, ic), false); /* now depending on the sign of the destination */ - size = AOP_SIZE(result) - AOP_SIZE(right); - /* Unsigned or not an integral type - right fill with zeros */ + size = result->aop->size - right->aop->size; + offset = right->aop->size - 1; + /* Unsigned or not an integral type - fill with zeros */ if (IS_BOOL(rtype) || !IS_SPEC(rtype) || SPEC_USIGN(rtype) || AOP_TYPE(right) == AOP_CRY) { - while (size--) - aopPut3(AOP(result), offset++, ASMOP_ZERO, 0); + cheapMove(result->aop, offset, right->aop, offset, true); + offset++; + genMove_o(result->aop, offset, ASMOP_ZERO, 0, size, true, false, false); } else { if (surviving_a && !pushed_a) _push(PAIR_AF), pushed_a = TRUE; - /* we need to extend the sign :{ */ - cheapMove(ASMOP_A, 0, AOP(right), AOP_SIZE(right) - 1); + + cheapMove(ASMOP_A, 0, AOP(right), offset, true); + if (AOP(right)->type != AOP_REG || AOP(result)->type != AOP_REG || + AOP(right)->aopu.aop_reg[offset] != AOP(result)->aopu.aop_reg[offset]) + cheapMove(AOP(result), offset, ASMOP_A, 0, true); + offset++; + + /* we need to extend the sign */ emit3(A_RLA, 0, 0); emit3(A_SBC, ASMOP_A, ASMOP_A); while (size--) - cheapMove(AOP(result), offset++, ASMOP_A, 0); + cheapMove(AOP(result), offset++, ASMOP_A, 0, true); } release: @@ -9512,23 +11418,11 @@ static void genCast(const iCode *ic) { /* genReceive - generate code for a receive iCode */ /*-----------------------------------------------------------------*/ static void genReceive(const iCode *ic) { - wassert(!regalloc_dry_run); - - if (isOperandInFarSpace(IC_RESULT(ic)) && - (OP_SYMBOL(IC_RESULT(ic))->isspilt || IS_TRUE_SYMOP(IC_RESULT(ic)))) { - wassert(0); - } else { - // PENDING: HACK - int size; - int i; - - aopOp(IC_RESULT(ic), ic, FALSE, FALSE); - size = AOP_SIZE(IC_RESULT(ic)); + operand *result = IC_RESULT(ic); + aopOp(result, ic, FALSE, FALSE); - for (i = 0; i < size; i++) { - aopPut(AOP(IC_RESULT(ic)), _fReceive[_G.receiveOffset++], i); - } - } + genMove(result->aop, ASMOP_RETURN, true, isPairDead(PAIR_HL, ic), + isPairDead(PAIR_DE, ic)); freeAsmop(IC_RESULT(ic), NULL); } @@ -9583,31 +11477,37 @@ static void genCritical(const iCode *ic) { emit2("!di"); regalloc_dry_run_cost += 1; } else if (IC_RESULT(ic)) { - aopOp(IC_RESULT(ic), ic, TRUE, FALSE); - aopPut3(AOP(IC_RESULT(ic)), 0, ASMOP_ZERO, 0); + aopOp(IC_RESULT(ic), ic, true, false); + cheapMove(IC_RESULT(ic)->aop, 0, ASMOP_ZERO, 0, true); if (!regalloc_dry_run) { - // get interrupt enable flag IFF2 into P/O - emit2("ld a,i"); - - // disable interrupt - emit2("!di"); + if (z80_opts.nmosZ80) { + emit2("call ___sdcc_critical_enter"); + } else { + // get interrupt enable flag IFF2 into P/O + emit2("ld a,i"); + // disable interrupt + emit2("!di"); + } // parity odd <==> P/O=0 <==> interrupt enable flag IFF2=0 - emit2("jp PO,!tlabel", labelKey2num(tlbl->key)); + emit2("jp PO, !tlabel", labelKey2num(tlbl->key)); } regalloc_dry_run_cost += 5; - aopPut3(AOP(IC_RESULT(ic)), 0, ASMOP_ONE, 0); + cheapMove(IC_RESULT(ic)->aop, 0, ASMOP_ONE, 0, true); if (!regalloc_dry_run) { emit2("!tlabeldef", labelKey2num((tlbl->key))); genLine.lineCurr->isLabel = 1; } freeAsmop(IC_RESULT(ic), NULL); } else { - // get interrupt enable flag IFF2 into P/O - emit2("ld a,i"); - - // disable interrupt - emit2("!di"); - regalloc_dry_run_cost += 2; + if (z80_opts.nmosZ80) + emit2("call ___sdcc_critical_enter"); + else { + // get interrupt enable flag IFF2 into P/O + emit2("ld a,i"); + // disable interrupt + emit2("!di"); + } + regalloc_dry_run_cost += 3; // save P/O flag if (!regalloc_dry_run) // _push unbalances _G.stack.pushed. _push(PAIR_AF); @@ -9634,7 +11534,7 @@ static void genEndCritical(const iCode *ic) { if (!regalloc_dry_run) { // don't enable interrupts if they were off before - emit2("jp Z,!tlabel", labelKey2num(tlbl->key)); + emit2("jp Z, !tlabel", labelKey2num(tlbl->key)); emit2("!ei"); emitLabelSpill(tlbl); } @@ -9649,7 +11549,7 @@ static void genEndCritical(const iCode *ic) { // parity odd <==> P/O=0 <==> interrupt enable flag IFF2 was 0 <==> // don't enable interrupts as they were off before if (!regalloc_dry_run) { - emit2("jp PO,!tlabel", labelKey2num(tlbl->key)); + emit2("jp PO, !tlabel", labelKey2num(tlbl->key)); emit2("!ei"); emit2("!tlabeldef", labelKey2num((tlbl->key))); genLine.lineCurr->isLabel = 1; @@ -9658,59 +11558,47 @@ static void genEndCritical(const iCode *ic) { } } -#if 0 // Disabled since it doesn't work for arrays of float. -enum -{ +enum { /** Maximum number of bytes to emit per line. */ DBEMIT_MAX_RUN = 8 }; /** Context for the byte output chunker. */ -typedef struct -{ +typedef struct { unsigned char buffer[DBEMIT_MAX_RUN]; int pos; } DBEMITCTX; - /** Flushes a byte chunker by writing out all in the buffer and reseting. */ -static void -_dbFlush (DBEMITCTX * self) -{ +static void _dbFlush(DBEMITCTX *self) { char line[256]; - if (self->pos > 0) - { - int i; - sprintf (line, ".db 0x%02X", self->buffer[0]); + if (self->pos > 0) { + int i; + sprintf(line, ".db !immed0x%02X", self->buffer[0]); - for (i = 1; i < self->pos; i++) - { - sprintf (line + strlen (line), ", 0x%02X", self->buffer[i]); - } - emit2 (line); + for (i = 1; i < self->pos; i++) { + sprintf(line + strlen(line), ", !immed0x%02X", self->buffer[i]); } + emit2(line); + } self->pos = 0; } /** Write out another byte, buffering until a decent line is generated. */ -static void -_dbEmit (DBEMITCTX * self, int c) -{ - if (self->pos == DBEMIT_MAX_RUN) - { - _dbFlush (self); - } +static void _dbEmit(DBEMITCTX *self, int c) { + if (self->pos == DBEMIT_MAX_RUN) { + _dbFlush(self); + } self->buffer[self->pos++] = c; } /** Context for a simple run length encoder. */ -typedef struct -{ +typedef struct { unsigned last; unsigned char buffer[128]; int pos; @@ -9718,32 +11606,24 @@ typedef struct int runLen; } RLECTX; -enum -{ - RLE_CHANGE_COST = 4, - RLE_MAX_BLOCK = 127 -}; +enum { RLE_CHANGE_COST = 4, RLE_MAX_BLOCK = 127 }; /** Flush the buffer of a run length encoder by writing out the run or data that it currently contains. */ -static void -_rleCommit (RLECTX * self) -{ +static void _rleCommit(RLECTX *self) { int i; - if (self->pos != 0) - { - DBEMITCTX db; - memset (&db, 0, sizeof (db)); + if (self->pos != 0) { + DBEMITCTX db; + memset(&db, 0, sizeof(db)); - emit2 (".db %u", self->pos); + emit2(".db !immed%u", self->pos); - for (i = 0; i < self->pos; i++) - { - _dbEmit (&db, self->buffer[i]); - } - _dbFlush (&db); + for (i = 0; i < self->pos; i++) { + _dbEmit(&db, self->buffer[i]); } + _dbFlush(&db); + } /* Reset */ self->pos = 0; } @@ -9768,59 +11648,46 @@ _rleCommit (RLECTX * self) that the next byte is repeated -n times. A zero terminates the chunks. */ -static void -_rleAppend (RLECTX * self, unsigned c) -{ +static void _rleAppend(RLECTX *self, unsigned c) { int i; - if (c != self->last) - { - /* The run has stopped. See if it is worthwhile writing it out - as a run. Note that the random data comes in as runs of - length one. - */ - if (self->runLen > RLE_CHANGE_COST) - { - /* Yes, worthwhile. */ - /* Commit whatever was in the buffer. */ - _rleCommit (self); - emit2 ("!db !immed-%u,!immedbyte", self->runLen, self->last); - } - else - { - /* Not worthwhile. Append to the end of the random list. */ - for (i = 0; i < self->runLen; i++) - { - if (self->pos >= RLE_MAX_BLOCK) - { - /* Commit. */ - _rleCommit (self); - } - self->buffer[self->pos++] = self->last; - } + if (c != self->last) { + /* The run has stopped. See if it is worthwhile writing it out + as a run. Note that the random data comes in as runs of + length one. + */ + if (self->runLen > RLE_CHANGE_COST) { + /* Yes, worthwhile. */ + /* Commit whatever was in the buffer. */ + _rleCommit(self); + emit2("!db !immed-%u, !immedbyte", self->runLen, self->last); + } else { + /* Not worthwhile. Append to the end of the random list. */ + for (i = 0; i < self->runLen; i++) { + if (self->pos >= RLE_MAX_BLOCK) { + /* Commit. */ + _rleCommit(self); } - self->runLen = 1; - self->last = c; + self->buffer[self->pos++] = self->last; + } } - else - { - if (self->runLen >= RLE_MAX_BLOCK) - { - /* Commit whatever was in the buffer. */ - _rleCommit (self); + self->runLen = 1; + self->last = c; + } else { + if (self->runLen >= RLE_MAX_BLOCK) { + /* Commit whatever was in the buffer. */ + _rleCommit(self); - emit2 ("!db !immed-%u,!immedbyte", self->runLen, self->last); - self->runLen = 0; - } - self->runLen++; + emit2("!db !immed-%u, !immedbyte", self->runLen, self->last); + self->runLen = 0; } + self->runLen++; + } } -static void -_rleFlush (RLECTX * self) -{ - _rleAppend (self, -1); - _rleCommit (self); +static void _rleFlush(RLECTX *self) { + _rleAppend(self, -1); + _rleCommit(self); self->pos = 0; self->last = 0; self->runLen = 0; @@ -9830,98 +11697,161 @@ _rleFlush (RLECTX * self) data. */ -static void -genArrayInit (iCode * ic) -{ +static void genArrayInit(iCode *ic) { literalList *iLoop; int ix; int elementSize = 0, eIndex, i; - unsigned val, lastVal; sym_link *type; RLECTX rle; + bool isBool = FALSE; + bool isFloat = FALSE; + bool saved_BC = FALSE; + bool saved_DE = FALSE; + bool saved_HL = FALSE; - memset (&rle, 0, sizeof (rle)); + memset(&rle, 0, sizeof(rle)); - aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp(IC_LEFT(ic), ic, FALSE, FALSE); - _saveRegsForCall (ic, 0); + if (!isPairDead(PAIR_HL, ic)) { + _push(PAIR_HL); + saved_HL = TRUE; + } + if (!isPairDead(PAIR_DE, ic)) { + _push(PAIR_DE); + saved_DE = TRUE; + } + if (!isPairDead(PAIR_BC, ic)) { + _push(PAIR_BC); + saved_BC = TRUE; + } - fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); - emit2 ("call __initrleblock"); + fetchPair(PAIR_DE, AOP(IC_LEFT(ic))); + emit2("call __initrleblock"); - type = operandType (IC_LEFT (ic)); + type = operandType(IC_LEFT(ic)); - if (type && type->next) - { - if (IS_SPEC (type->next) || IS_PTR (type->next)) - { - elementSize = getSize (type->next); - } - else if (IS_ARRAY (type->next) && type->next->next) - { - elementSize = getSize (type->next->next); - } - else - { - printTypeChainRaw (type, NULL); - wassertl (0, "Can't determine element size in genArrayInit."); - } - } - else - { - wassertl (0, "Can't determine element size in genArrayInit."); + if (type && type->next) { + if (IS_SPEC(type->next) || IS_PTR(type->next)) { + elementSize = getSize(type->next); + isBool = IS_BOOL(type->next); + isFloat = IS_FLOAT(type->next); + } else if (IS_ARRAY(type->next) && type->next->next) { + elementSize = getSize(type->next->next); + isBool = IS_BOOL(type->next->next); + isFloat = IS_FLOAT(type->next->next); + } else { + printTypeChainRaw(type, NULL); + wassertl(0, "Can't determine element size in genArrayInit."); } + } else { + wassertl(0, "Can't determine element size in genArrayInit."); + } - wassertl ((elementSize > 0) && (elementSize <= 4), "Illegal element size in genArrayInit."); + wassertl((elementSize > 0) && (elementSize <= 8), + "Illegal element size in genArrayInit."); - iLoop = IC_ARRAYILIST (ic); - lastVal = (unsigned) - 1; + iLoop = IC_ARRAYILIST(ic); /* Feed all the bytes into the run length encoder which will handle the actual output. This works well for mixed char data, and for random int and long data. */ - while (iLoop) - { - ix = iLoop->count; + while (iLoop) { + ix = iLoop->count; - for (i = 0; i < ix; i++) - { - for (eIndex = 0; eIndex < elementSize; eIndex++) - { - val = (((int) iLoop->literalValue) >> (eIndex * 8)) & 0xff; - _rleAppend (&rle, val); - } - } + for (i = 0; i < ix; i++) { + union { + unsigned char c[sizeof(unsigned long long)]; + float f; + unsigned long long ull; + } buf; + if (isFloat) { + if (iLoop->isFloat) + buf.f = iLoop->value.f64; + else + buf.f = iLoop->value.ull; + } else { + if (iLoop->isFloat) + buf.ull = (isBool) ? !!iLoop->value.f64 + : (unsigned long long)iLoop->value.f64; + else + buf.ull = (isBool) ? !!iLoop->value.ull : iLoop->value.ull; + } - iLoop = iLoop->next; +#ifdef WORDS_BIGENDIAN + for (eIndex = elementSize - 1; eIndex >= 0; eIndex--) +#else + for (eIndex = 0; eIndex < elementSize; eIndex++) +#endif + _rleAppend(&rle, buf.c[eIndex]); } - _rleFlush (&rle); + iLoop = iLoop->next; + } + + _rleFlush(&rle); /* Mark the end of the run. */ - emit2 (".db 0"); + emit2(".db !immed0"); + + spillCached(); + + if (saved_BC) + _pop(PAIR_BC); - _restoreRegsAfterCall (); + if (saved_DE) + _pop(PAIR_DE); - spillCached (); + if (saved_HL) + _pop(PAIR_HL); - freeAsmop (IC_LEFT (ic), NULL, ic); + freeAsmop(IC_LEFT(ic), NULL); } -#endif static void setupForMemcpy(const iCode *ic, const operand *to, - const operand *from) { + const operand *from, const operand *count) { /* Both are in regs. Let regMove() do the shuffling. */ if (AOP_TYPE(to) == AOP_REG && AOP_TYPE(from) == AOP_REG) { - const short larray[4] = {E_IDX, D_IDX, L_IDX, H_IDX}; + const short larray[6] = {E_IDX, D_IDX, L_IDX, H_IDX, C_IDX, B_IDX}; + short oparray[6]; + oparray[0] = to->aop->aopu.aop_reg[0]->rIdx; + oparray[1] = to->aop->aopu.aop_reg[1]->rIdx; + oparray[2] = from->aop->aopu.aop_reg[0]->rIdx; + oparray[3] = from->aop->aopu.aop_reg[1]->rIdx; + if (count && count->aop->type == AOP_REG) { + oparray[4] = count->aop->aopu.aop_reg[0]->rIdx; + oparray[5] = count->aop->aopu.aop_reg[1]->rIdx; + } + + regMove(larray, oparray, 4 + (count && count->aop->type == AOP_REG) * 2, + false); + } else if (AOP_TYPE(to) == AOP_REG && count && AOP_TYPE(count) == AOP_REG) { + const short larray[4] = {E_IDX, D_IDX, C_IDX, B_IDX}; short oparray[4]; - oparray[0] = AOP(to)->aopu.aop_reg[0]->rIdx; - oparray[1] = AOP(to)->aopu.aop_reg[1]->rIdx; - oparray[2] = AOP(from)->aopu.aop_reg[0]->rIdx; - oparray[3] = AOP(from)->aopu.aop_reg[1]->rIdx; + oparray[0] = to->aop->aopu.aop_reg[0]->rIdx; + oparray[1] = to->aop->aopu.aop_reg[1]->rIdx; + oparray[2] = count->aop->aopu.aop_reg[0]->rIdx; + oparray[3] = count->aop->aopu.aop_reg[1]->rIdx; - regMove(larray, oparray, 4, FALSE); + regMove(larray, oparray, 4, false); + + fetchPair(PAIR_HL, from->aop); + } else if (AOP_TYPE(from) == AOP_REG && count && AOP_TYPE(count) == AOP_REG) { + const short larray[4] = {L_IDX, H_IDX, C_IDX, B_IDX}; + short oparray[4]; + oparray[0] = from->aop->aopu.aop_reg[0]->rIdx; + oparray[1] = from->aop->aopu.aop_reg[1]->rIdx; + oparray[2] = count->aop->aopu.aop_reg[0]->rIdx; + oparray[3] = count->aop->aopu.aop_reg[1]->rIdx; + + regMove(larray, oparray, 4, false); + + fetchPair(PAIR_DE, to->aop); + } else if (count && AOP_TYPE(count) == AOP_REG) { + fetchPair(PAIR_BC, count->aop); + fetchPair(PAIR_DE, to->aop); + fetchPair(PAIR_HL, from->aop); } else { /* DE is free. Write it first. */ if (AOP_TYPE(from) != AOP_REG || @@ -9948,15 +11878,15 @@ static void setupForMemcpy(const iCode *ic, const operand *to, (AOP_TYPE(from) != AOP_REG || AOP(from)->aopu.aop_reg[0]->rIdx != L_IDX && AOP(from)->aopu.aop_reg[1]->rIdx != L_IDX)) { - cheapMove(ASMOP_L, 0, AOP(from), 0); + cheapMove(ASMOP_L, 0, AOP(from), 0, true); fetchPair(PAIR_DE, AOP(to)); - cheapMove(ASMOP_H, 0, AOP(from), 1); + cheapMove(ASMOP_H, 0, AOP(from), 1, true); } /* H is free, but L is not. */ else { - cheapMove(ASMOP_H, 0, AOP(from), 1); + cheapMove(ASMOP_H, 0, AOP(from), 1, true); fetchPair(PAIR_DE, AOP(to)); - cheapMove(ASMOP_L, 0, AOP(from), 0); + cheapMove(ASMOP_L, 0, AOP(from), 0, true); } } } @@ -9972,15 +11902,16 @@ static void genBuiltInMemcpy(const iCode *ic, int nparams, operand **pparams) { wassertl(!IS_GB, "Built-in memcpy() not available on gbz80."); wassertl(nparams == 3, "Built-in memcpy() must have three parameters."); - /* Check for zero length copy. */ - wassertl(AOP_TYPE(pparams[2]) == AOP_LIT, - "Last parameter to builtin memcpy() must be literal."); count = pparams[2]; from = pparams[1]; to = pparams[0]; - if (!(n = (unsigned int)ulFromVal(AOP(pparams[2])->aopu.aop_lit))) + if (pparams[2]->aop->type != AOP_LIT) + n = UINT_MAX; + else if (!(n = (unsigned int)ulFromVal( + AOP(pparams[2]) + ->aopu.aop_lit))) /* Check for zero length copy. */ goto done; if (!isPairDead(PAIR_HL, ic)) { @@ -9996,7 +11927,7 @@ static void genBuiltInMemcpy(const iCode *ic, int nparams, operand **pparams) { saved_BC = TRUE; } - setupForMemcpy(ic, to, from); + setupForMemcpy(ic, to, from, count); if (n == 1) { emit2("ld a, (hl)"); @@ -10012,10 +11943,55 @@ static void genBuiltInMemcpy(const iCode *ic, int nparams, operand **pparams) { emit2("inc bc"); regalloc_dry_run_cost++; } + } else if (n <= 4 && IS_Z80 && optimize.codeSpeed || + (IS_R2K || IS_R2KA) && n <= 5) { + for (unsigned int i = 0; i < n; i++) + emit2("ldi"); + regalloc_dry_run_cost += n * 2; } else { - fetchPair(PAIR_BC, AOP(count)); - emit2("ldir"); - regalloc_dry_run_cost += 2; + symbol *tlbl = 0; + if (count->aop->type != + AOP_REG) // If in reg: Has been fetched early by setupForMemcpy() above. + fetchPair(PAIR_BC, count->aop); + if (count->aop->type != AOP_LIT) { + if (!regalloc_dry_run) { + tlbl = newiTempLabel(0); + emit2("ld a, b"); + emit2("or a, c"); + emit2("jp Z, !tlabel", labelKey2num(tlbl->key)); + } + regalloc_dry_run_cost += 5; + } + if ((IS_R2K || IS_R2KA) && optimize.codeSpeed && + n != UINT_MAX) // Work around ldir wait state bug, but care for speed + { + wassert(n > 3); + if (n % 2) { + emit2("ldi"); + regalloc_dry_run_cost += 2; + } + if (!regalloc_dry_run) { + const symbol *tlbl2 = newiTempLabel(0); + emitLabel(tlbl2); + emit2("ldi"); + emit2("ldi"); + emit2("jp LO, !tlabel", labelKey2num(tlbl2->key)); + } + regalloc_dry_run_cost += 7; + } else if (IS_R2K || IS_R2KA) // Work around ldir wait state bug. + { + if (!regalloc_dry_run) { + const symbol *tlbl2 = newiTempLabel(0); + emitLabel(tlbl2); + emit2("ldi"); + emit2("jp LO, !tlabel", labelKey2num(tlbl2->key)); + } + regalloc_dry_run_cost += 5; + } else { + emit2("ldir"); + regalloc_dry_run_cost += 2; + } + emitLabel(tlbl); } spillPair(PAIR_HL); @@ -10047,7 +12023,7 @@ static void setupForMemset(const iCode *ic, const operand *dst, AOP(c)->aopu.aop_reg[0]->rIdx == H_IDX); if (early_a) - cheapMove(ASMOP_A, 0, AOP(c), 0); + cheapMove(ASMOP_A, 0, AOP(c), 0, true); oparray[0] = AOP(dst)->aopu.aop_reg[0]->rIdx; oparray[1] = AOP(dst)->aopu.aop_reg[1]->rIdx; @@ -10055,9 +12031,9 @@ static void setupForMemset(const iCode *ic, const operand *dst, regMove(larray, oparray, 2, early_a); if (!early_a) - cheapMove(ASMOP_A, 0, AOP(c), 0); + cheapMove(ASMOP_A, 0, AOP(c), 0, true); } else if (AOP_TYPE(c) == AOP_REG && requiresHL(AOP(c))) { - cheapMove(ASMOP_A, 0, AOP(c), 0); + cheapMove(ASMOP_A, 0, AOP(c), 0, true); if (AOP_TYPE(dst) == AOP_EXSTK) _push(PAIR_AF); fetchPair(PAIR_HL, AOP(dst)); @@ -10068,7 +12044,7 @@ static void setupForMemset(const iCode *ic, const operand *dst, if (!direct_c) { if (requiresHL(AOP(c))) _push(PAIR_HL); - cheapMove(ASMOP_A, 0, AOP(c), 0); + cheapMove(ASMOP_A, 0, AOP(c), 0, true); if (requiresHL(AOP(c))) _pop(PAIR_HL); } @@ -10098,9 +12074,10 @@ static void genBuiltInMemset(const iCode *ic, int nParams, operand **pparams) { aopOp(dst, ic, FALSE, FALSE); aopOp(n, ic, FALSE, FALSE); - wassertl(AOP_TYPE(n) == AOP_LIT, + wassertl(n->aop->type == AOP_LIT, "Last parameter to builtin memset() must be literal."); - if (!(size = ulFromVal(AOP(n)->aopu.aop_lit))) + + if (n->aop->type != AOP_LIT || !(size = ulFromVal(n->aop->aopu.aop_lit))) goto done; direct_c = @@ -10158,7 +12135,7 @@ static void genBuiltInMemset(const iCode *ic, int nParams, operand **pparams) { saved_BC = TRUE; } - setupForMemset(ic, dst, c, direct_c); + setupForMemset(ic, dst, c, direct_cl); emit2("ld b, !immedbyte", double_loop ? (size / 2 + size % 2) : size); regalloc_dry_run_cost += 2; @@ -10171,12 +12148,12 @@ static void genBuiltInMemset(const iCode *ic, int nParams, operand **pparams) { if (!regalloc_dry_run) { emitLabel(tlbl1); - emit2("ld (hl), %s", aopGet(direct_c ? AOP(c) : ASMOP_A, 0, FALSE)); + emit2("ld (hl), %s", aopGet(direct_cl ? AOP(c) : ASMOP_A, 0, FALSE)); emit2("inc hl"); if (double_loop) { if (size % 2) emitLabel(tlbl2); - emit2("ld (hl), %s", aopGet(direct_c ? AOP(c) : ASMOP_A, 0, FALSE)); + emit2("ld (hl), %s", aopGet(direct_cl ? AOP(c) : ASMOP_A, 0, FALSE)); emit2("inc hl"); } emit2("djnz !tlabel", labelKey2num(tlbl1->key)); @@ -10198,7 +12175,7 @@ static void genBuiltInMemset(const iCode *ic, int nParams, operand **pparams) { } if (indirect_c) { fetchPair(PAIR_DE, AOP(dst)); - emit2("ld hl, #%s", AOP(c)->aopu.aop_dir); + emit2("ld hl, !hashedstr", AOP(c)->aopu.aop_dir); regalloc_dry_run_cost += 3; } else { setupForMemset(ic, dst, c, direct_c); @@ -10219,8 +12196,21 @@ static void genBuiltInMemset(const iCode *ic, int nParams, operand **pparams) { } } emit2("ld bc, !immedword", size - preinc); - emit2(IS_R3KA ? "lsidr" : "ldir"); - regalloc_dry_run_cost += 5; + regalloc_dry_run_cost += 3; + if (IS_R2K || IS_R2KA) // Work around ldir wait state bug that affects + // copies between different types of memory. + { + if (!regalloc_dry_run) { + const symbol *tlbl2 = newiTempLabel(0); + emitLabel(tlbl2); + emit2("ldi"); + emit2("jp LO, !tlabel", labelKey2num(tlbl2->key)); + } + regalloc_dry_run_cost += 5; + } else { + emit2(IS_R3KA ? "lsidr" : "ldir"); + regalloc_dry_run_cost += 2; + } } done: @@ -10275,7 +12265,7 @@ static void genBuiltInStrcpy(const iCode *ic, int nParams, operand **pparams) { saved_DE = TRUE; } - setupForMemcpy(ic, dst, src); + setupForMemcpy(ic, dst, src, 0); emit3(A_XOR, ASMOP_A, ASMOP_A); if (SomethingReturned) @@ -10306,7 +12296,7 @@ static void genBuiltInStrcpy(const iCode *ic, int nParams, operand **pparams) { _pop(PAIR_HL); } else { _pop(PAIR_HL); - assignResultValue(IC_RESULT(ic)); + genMove(IC_RESULT(ic)->aop, ASMOP_RETURN, true, true, true); restoreRegs(0, saved_DE, saved_BC, saved_HL, IC_RESULT(ic)); } @@ -10350,7 +12340,7 @@ static void genBuiltInStrncpy(const iCode *ic, int nparams, operand **pparams) { saved_DE = TRUE; } - setupForMemcpy(ic, s1, s2); + setupForMemcpy(ic, s1, s2, 0); fetchPair(PAIR_BC, AOP(n)); @@ -10362,12 +12352,14 @@ static void genBuiltInStrncpy(const iCode *ic, int nparams, operand **pparams) { emitLabel(tlbl2); emit2("cp a, (hl)"); emit2("ldi"); - emit2("jp PO, !tlabel", labelKey2num(tlbl1->key)); + emit2(IS_RAB ? "jp LZ, !tlabel" : "jp PO, !tlabel", + labelKey2num(tlbl1->key)); emit2("jr NZ, !tlabel", labelKey2num(tlbl2->key)); emitLabel(tlbl3); emit2("dec hl"); emit2("ldi"); - emit2("jp PE, !tlabel", labelKey2num(tlbl3->key)); + emit2(IS_RAB ? "jp LO, !tlabel" : "jp PE, !tlabel", + labelKey2num(tlbl3->key)); emitLabel(tlbl1); } regalloc_dry_run_cost += 14; @@ -10449,7 +12441,7 @@ static void genBuiltInStrchr(const iCode *ic, int nParams, operand **pparams) { } if (!direct_c) - cheapMove(aop_c, 0, AOP(c), 0); + cheapMove(aop_c, 0, AOP(c), 0, true); fetchPair(pair, AOP(s)); if (!regalloc_dry_run) @@ -10568,15 +12560,11 @@ static void genZ80iCode(iCode *ic) { break; case CALL: + case PCALL: emitDebug("; genCall"); genCall(ic); break; - case PCALL: - emitDebug("; genPcall"); - genPcall(ic); - break; - case FUNCTION: emitDebug("; genFunction"); genFunction(ic); @@ -10757,18 +12745,16 @@ static void genZ80iCode(iCode *ic) { if (ic->builtinSEND) { emitDebug("; genBuiltIn"); genBuiltIn(ic); - } else if (!regalloc_dry_run) { - emitDebug("; addSet"); - addSet(&_G.sendSet, ic); + } else { + emitDebug("; genSend"); + genSend(ic); } break; -#if 0 - case ARRAYINIT: - emitDebug ("; genArrayInit"); - genArrayInit (ic); - break; -#endif + case ARRAYINIT: + emitDebug("; genArrayInit"); + genArrayInit(ic); + break; case DUMMY_READ_VOLATILE: emitDebug("; genDummyRead"); @@ -10793,15 +12779,6 @@ unsigned char dryZ80iCode(iCode *ic) { regalloc_dry_run = TRUE; regalloc_dry_run_cost = 0; - /* Hack */ - if (IS_GB) { - _fReturn = _gbz80_return; - _fTmp = _gbz80_return; - } else { - _fReturn = _z80_return; - _fTmp = _z80_return; - } - initGenLineElement(); _G.omitFramePtr = should_omit_frame_ptr; @@ -10822,7 +12799,7 @@ unsigned char dryZ80iCode(iCode *ic) { #ifdef DEBUG_DRY_COST static void dryZ80Code(iCode *lic) { iCode *ic; - + last_dry_run = true; for (ic = lic; ic; ic = ic->next) if (ic->op != FUNCTION && ic->op != ENDFUNCTION && ic->op != LABEL && ic->op != GOTO && ic->op != INLINEASM) @@ -10843,17 +12820,14 @@ void genZ80Code(iCode *lic) { int cln = 0; regalloc_dry_run = FALSE; - /* Hack */ - if (IS_GB) { - _fReturn = _gbz80_return; - _fTmp = _gbz80_return; - } else { - _fReturn = _z80_return; - _fTmp = _z80_return; - } - initGenLineElement(); + memset(z80_regs_used_as_parms_in_calls_from_current_function, 0, + sizeof(bool) * (IYH_IDX + 1)); + z80_symmParm_in_calls_from_current_function = TRUE; + memset(z80_regs_preserved_in_calls_from_current_function, 0, + sizeof(bool) * (IYH_IDX + 1)); + for (ic = lic; ic; ic = ic->next) ic->generated = FALSE; @@ -10861,7 +12835,7 @@ void genZ80Code(iCode *lic) { for (ic = lic; ic; ic = ic->next) { if (ic->lineno && cln != ic->lineno) { if (options.debug) - emit2(".map %s, %d, \"%s\"", ic->filename, ic->lineno, + emit2(";%s:%d: %s", ic->filename, ic->lineno, printCLine(ic->filename, ic->lineno)); cln = ic->lineno; } @@ -10872,8 +12846,10 @@ void genZ80Code(iCode *lic) { } regalloc_dry_run_cost = 0; genZ80iCode(ic); - /*emitDebug("; iCode %d total cost: %d", ic->key, - * (int)(regalloc_dry_run_cost));*/ + +#ifdef DEBUG_DRY_COST + emit2("; iCode %d total cost: %d\n", ic->key, regalloc_dry_run_cost); +#endif } /* now we are ready to call the diff --git a/src/backend/gen.h b/src/backend/gen.h index d9d40facb..9f36c727f 100644 --- a/src/backend/gen.h +++ b/src/backend/gen.h @@ -47,12 +47,6 @@ typedef enum { AOP_IY, /* Is pointed to by HL */ AOP_HL, - /* Is in A */ - AOP_ACC, - /* Is in H and L */ - AOP_HLREG, - /* Simple literal. */ - AOP_SIMPLELIT, /* Is in the extended stack pointer (IY on the Z80) */ AOP_EXSTK, /* Is referenced by a pointer in a register pair. */ @@ -79,9 +73,10 @@ typedef struct asmop { char *aop_immd; /* if immediate others are implied */ int aop_stk; /* stack offset when AOP_STK */ const char *aop_str[4]; /* just a string array containing the location */ - unsigned long aop_simplelit; /* Just the value. */ - int aop_pairId; /* The pair ID */ + int aop_pairId; /* The pair ID */ } aopu; + signed char regs[9]; // Byte of this aop that is in the register. -1 if no + // byte of this aop is in the reg. } asmop; void genZ80Code(iCode *); diff --git a/src/backend/main.c b/src/backend/main.c index e8083da71..0bf77eb1c 100644 --- a/src/backend/main.c +++ b/src/backend/main.c @@ -22,10 +22,10 @@ what you give them. Help stamp out software-hoarding! -------------------------------------------------------------------------*/ -#include "SDCCargs.h" -#include "SDCCsystem.h" -#include "SDCCutil.h" -#include "dbuf_string.h" +#include "../SDCCargs.h" +#include "../SDCCsystem.h" +#include "../SDCCutil.h" +#include "../util/dbuf_string.h" #include "z80.h" #include @@ -33,6 +33,7 @@ #define OPTION_BA "-ba" #define OPTION_CODE_SEG "--codeseg" #define OPTION_CONST_SEG "--constseg" +#define OPTION_DATA_SEG "--dataseg" #define OPTION_CALLEE_SAVES_BC "--callee-saves-bc" #define OPTION_PORTMODE "--portmode=" #define OPTION_ASM "--asm=" @@ -40,6 +41,9 @@ #define OPTION_RESERVE_IY "--reserve-regs-iy" #define OPTION_OLDRALLOC "--oldralloc" #define OPTION_FRAMEPOINTER "--fno-omit-frame-pointer" +#define OPTION_EMIT_EXTERNS "--emit-externs" +#define OPTION_LEGACY_BANKING "--legacy-banking" +#define OPTION_NMOS_Z80 "--nmos-z80" static char _z80_defaultRules[] = { #include "peeph-z80.rul" @@ -55,6 +59,8 @@ static OPTION _z80_options[] = { " use this name for the code segment", CLAT_STRING}, {0, OPTION_CONST_SEG, &options.const_seg, " use this name for the const segment", CLAT_STRING}, + {0, OPTION_DATA_SEG, &options.data_seg, + " use this name for the data segment", CLAT_STRING}, {0, OPTION_NO_STD_CRT0, &options.no_std_crt0, "For the z80 do not link default crt0.rel"}, {0, OPTION_RESERVE_IY, &z80_opts.reserveIY, @@ -62,6 +68,11 @@ static OPTION _z80_options[] = { {0, OPTION_OLDRALLOC, &options.oldralloc, "Use old register allocator"}, {0, OPTION_FRAMEPOINTER, &z80_opts.noOmitFramePtr, "Do not omit frame pointer"}, + {0, OPTION_EMIT_EXTERNS, NULL, "Emit externs list in generated asm"}, + {0, OPTION_LEGACY_BANKING, &z80_opts.legacyBanking, + "Use legacy method to call banked functions"}, + {0, OPTION_NMOS_Z80, &z80_opts.nmosZ80, + "Generate workaround for NMOS Z80 when saving IFF2"}, {0, NULL}}; typedef enum { @@ -76,26 +87,26 @@ static struct { ASM_TYPE asmType; /* determine if we can register a parameter */ int regParams; + bool z88dk_fastcall; } _G; static char *_keywords[] = { - "sfr", "nonbanked", "banked", - "at", //.p.t.20030714 adding support for 'sfr at ADDR' construct - "_naked", //.p.t.20030714 adding support for '_naked' functions - "critical", "interrupt", NULL}; + "sfr", "nonbanked", "banked", + "at", "_naked", "critical", + "interrupt", "z88dk_fastcall", "z88dk_callee", + "smallc", "z88dk_shortcall", "z88dk_params_offset", + NULL}; extern PORT z80_port; -extern PORT r2k_port; -extern PORT gbz80_port; #include "mappings.i" static builtins _z80_builtins[] = { - {"__builtin_memcpy", "vg*", 3, {"vg*", "Cvg*", "ui"}}, + {"__builtin_memcpy", "vg*", 3, {"vg*", "Cvg*", "Ui"}}, {"__builtin_strcpy", "cg*", 2, {"cg*", "Ccg*"}}, - {"__builtin_strncpy", "cg*", 3, {"cg*", "Ccg*", "ui"}}, + {"__builtin_strncpy", "cg*", 3, {"cg*", "Ccg*", "Ui"}}, {"__builtin_strchr", "cg*", 2, {"Ccg*", "i"}}, - {"__builtin_memset", "vg*", 3, {"vg*", "i", "ui"}}, + {"__builtin_memset", "vg*", 3, {"vg*", "i", "Ui"}}, {NULL, NULL, 0, {NULL}}}; static void _z80_init(void) { @@ -103,22 +114,23 @@ static void _z80_init(void) { asm_addTree(&_asxxxx_z80); } -static void _reset_regparm(void) { _G.regParams = 0; } +static void _reset_regparm(struct sym_link *funcType) { + _G.regParams = 0; + _G.z88dk_fastcall = IFFUNC_ISZ88DK_FASTCALL(funcType); + if (_G.z88dk_fastcall && IFFUNC_HASVARARGS(funcType)) + werror(E_Z88DK_FASTCALL_PARAMETERS); +} static int _reg_parm(sym_link *l, bool reentrant) { - if (options.noRegParams) { - return FALSE; - } else { - if (!IS_REGISTER(l) || getSize(l) > 2) { - return FALSE; - } - if (_G.regParams == 2) { - return FALSE; - } else { - _G.regParams++; - return TRUE; - } + if (_G.z88dk_fastcall) { + if (_G.regParams) + werror(E_Z88DK_FASTCALL_PARAMETERS); + if (getSize(l) > 4) + werror(E_Z88DK_FASTCALL_PARAMETER); + _G.regParams++; + return TRUE; } + return FALSE; } enum { @@ -155,7 +167,7 @@ static int do_pragma(int id, const char *name, const char *cp) { break; case ASM_TYPE_RGBDS: - dbuf_printf(&buffer, "CODE,BANK[%d]", token.val.int_val); + dbuf_printf(&buffer, "ROMX,BANK[%d]", token.val.int_val); break; case ASM_TYPE_ISAS: @@ -182,6 +194,7 @@ static int do_pragma(int id, const char *name, const char *cp) { } dbuf_c_str(&buffer); + options.code_seg = (char *)dbuf_detach(&buffer); } break; case P_PORTMODE: { /*.p.t.20030716 - adding pragma to manipulate z80 i/o port @@ -378,14 +391,35 @@ static const char *_getRegName(const struct reg_info *reg) { return "err"; } +static int _getRegByName(const char *name) { + if (!strcmp(name, "a")) + return 0; + if (!strcmp(name, "c")) + return 1; + if (!strcmp(name, "b")) + return 2; + if (!strcmp(name, "e")) + return 3; + if (!strcmp(name, "d")) + return 4; + if (!strcmp(name, "l")) + return 5; + if (!strcmp(name, "h")) + return 6; + if (!strcmp(name, "iyl")) + return 7; + if (!strcmp(name, "iyh")) + return 8; + return -1; +} + static bool _hasNativeMulFor(iCode *ic, sym_link *left, sym_link *right) { sym_link *test = NULL; int result_size = IS_SYMOP(IC_RESULT(ic)) ? getSize(OP_SYM_TYPE(IC_RESULT(ic))) : 4; - if (ic->op != '*') { - return FALSE; - } + if (ic->op != '*') + return (false); if (IS_LITERAL(left)) test = left; @@ -394,21 +428,18 @@ static bool _hasNativeMulFor(iCode *ic, sym_link *left, sym_link *right) { /* 8x8 unsigned multiplication code is shorter than call overhead for the multiplication routine. */ else if (IS_CHAR(right) && IS_UNSIGNED(right) && IS_CHAR(left) && - IS_UNSIGNED(left)) { - return TRUE; - } + IS_UNSIGNED(left)) + return (true); /* Same for any multiplication with 8 bit result. */ - else if (result_size == 1) { - return TRUE; - } else { - return FALSE; - } + else if (result_size == 1) + return (true); + else + return (false); - if (getSize(test) <= 2) { - return TRUE; - } + if (getSize(test) <= 2) + return (true); - return FALSE; + return (false); } /* Indicate which extended bit operations this port supports */ @@ -452,8 +483,7 @@ PORT z80_port = { "Zilog Z80", /* Target name */ NULL, /* Processor name */ { - glue, - FALSE, + glue, FALSE, NO_MODEL, NO_MODEL, NULL, /* model == target */ }, { /* Assembler */ _z80AsmCmd, NULL, "-o", /* Options with debug */ @@ -476,10 +506,13 @@ PORT z80_port = { z80notUsed, z80canAssign, z80notUsedFrom, + z80symmParmStack, + z80canJoinRegs, + z80canSplitReg, }, - {/* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, - max */ - 1, 2, 2, 4, 8, 2, 2, 2, 1, 4, 4}, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func + ptr, banked func ptr, bit, float */ + {1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4}, /* tags for generic pointers */ {0x00, 0x40, 0x60, 0x80}, /* far, near, xstack, code */ { @@ -495,7 +528,7 @@ PORT z80_port = { "GSINIT", /* static initialization */ NULL, /* overlay */ "GSFINAL", - "CODE", + "HOME", NULL, /* xidata */ NULL, /* xinit */ NULL, /* const_name */ @@ -511,19 +544,18 @@ PORT z80_port = { 1 /* No fancy alignments supported. */ }, {NULL, NULL}, - {-1, 0, 0, 4, 0, 2}, - /* Z80 has no native mul/div commands */ - {0, -1}, + {-1, 0, 0, 4, 0, 3, 0}, + {-1, FALSE}, {z80_emitDebuggerSymbol}, { - 255, /* maxCount */ + 256, /* maxCount */ 3, /* sizeofElement */ - /* The rest of these costs are bogus. They approximate */ - /* the behavior of src/SDCCicode.c 1.207 and earlier. */ - {4, 4, 4}, /* sizeofMatchJump[] */ - {0, 0, 0}, /* sizeofRangeCompare[] */ - 0, /* sizeofSubtract */ - 3, /* sizeofDispatch */ + {6, 7, + 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, + 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ }, "_", _z80_init, @@ -535,8 +567,9 @@ PORT z80_port = { z80_assignRegisters, _getRegName, NULL, + NULL, _keywords, - 0, /* no assembler preamble */ + NULL, /* no assembler preamble */ NULL, /* no genAssemblerEnd */ 0, /* no local IVT generation code */ 0, /* no genXINIT code */ diff --git a/src/backend/mappings.i b/src/backend/mappings.i index b89974c5c..2facb7685 100644 --- a/src/backend/mappings.i +++ b/src/backend/mappings.i @@ -1,46 +1,3 @@ -static const ASM_MAPPING _asxxxx_gb_mapping[] = { - /* We want to prepend the _ */ - { "area", ".area _%s" }, - { "areacode", ".area _%s" }, - { "areadata", ".area _%s" }, - { "areahome", ".area _%s" }, - { "functionlabeldef", "%s:" }, - { "*hl", "(hl)" }, - { "di", "di" }, - { "ei", "ei" }, - /*{ "ldahli", "ldi\ta,(hl)" }, use when assembler update is complete*/ - {"ldahli", "ld\ta,(hl)\ninc\thl"}, - { "ldahlsp", "ldhl\tsp,#%d" }, - { "ldaspsp", "add sp, #%d" }, - { "*pair", "(%s)" }, - { "enter", "" }, - { "enterx", - "add sp, #-%d" }, - { "pusha", - "push af\n" - "push bc\n" - "push de\n" - "push hl" - }, - { "popa", - "pop hl\n" - "pop de\n" - "pop bc\n" - "pop af" - }, - { "adjustsp", "lda sp,-(sp + %d)" }, - { "fileprelude", "" }, - { "profileenter", - "ld a,#3\n" - "rst\t0x08" - }, - { "profileexit", - "ld a,#4\n" - "rst\t0x08" - }, - { NULL, NULL } -}; - static const ASM_MAPPING _asxxxx_z80_mapping[] = { /* We want to prepend the _ */ { "area", ".area _%s" }, @@ -50,414 +7,55 @@ static const ASM_MAPPING _asxxxx_z80_mapping[] = { { "*ixx", "(ix + %d)" }, { "*iyx", "(iy + %d)" }, { "*hl", "(hl)" }, + { "jphl", "jp (hl)" }, { "di", "di" }, { "ei", "ei" }, - { "ldahli", - "ld a,(hl)\n" - "inc\thl" }, - { "ldahlsp", - "ld hl,#%d\n" - "add\thl,sp" }, - { "ldaspsp", - "ld iy,#%d\n" - "add\tiy,sp\n" - "ld\tsp,iy" }, - { "*pair", "(%s)" }, - { "*offspair", "%s" }, - { "enter", - "push\tix\n" - "ld\tix,#0\n" - "add\tix,sp" }, - { "pusha", - "push af\n" - "push\tbc\n" - "push\tde\n" - "push\thl\n" - "push\tiy" - }, - { "popa", - "pop iy\n" - "pop\thl\n" - "pop\tde\n" - "pop\tbc\n" - "pop\taf" - }, - { "adjustsp", "lda sp,-(sp + %d)" }, - { "profileenter", - "ld a,#3\n" - "rst\t0x08" - }, - { "profileexit", - "ld a,#4\n" - "rst\t0x08" - }, - { NULL, NULL } -}; - -static const ASM_MAPPING _asxxxx_r2k_mapping[] = { - /* We want to prepend the _ */ - { "area", ".area _%s" }, - { "areacode", ".area _%s" }, - { "areadata", ".area _%s" }, - { "areahome", ".area _%s" }, - { "*ixx", "(ix + %d)" }, - { "*iyx", "(iy + %d)" }, - { "*hl", "(hl)" }, - { "di", "ipset3" }, - { "ei", "ipres" }, - { "ldahli", - "ld a,(hl)\n" - "inc\thl" }, - { "ldahlsp", - "ld hl,#%d\n" - "add\thl,sp" }, - { "ldaspsp", - "ld iy,#%d\n" - "add\tiy,sp\n" - "ld\tsp,iy" }, - { "*pair", "(%s)" }, - { "enter", - "push\tix\n" - "ld\tix,#0\n" - "add\tix,sp" }, - { "pusha", - "push af\n" - "push\tbc\n" - "push\tde\n" - "push\thl\n" - "push\tiy" + { "globalfunctionlabeldef", "%s:" }, + { "ldahli", + "ld a, (hl)\n" + "inc\thl" }, + { "ldahlsp", + "ld hl, #%d\n" + "add\thl, sp" }, + { "ldaspsp", + "ld iy,#%d\n" + "add\tiy,sp\n" + "ld\tsp,iy" }, + { "mems", "(%s)" }, + { "enter", + "push\tix\n" + "ld\tix,#0\n" + "add\tix,sp" }, + { "enters", + "call\t___sdcc_enter_ix\n" }, + { "pusha", + "push af\n" + "push\tbc\n" + "push\tde\n" + "push\thl\n" + "push\tiy" }, { "popa", - "pop iy\n" - "pop\thl\n" - "pop\tde\n" - "pop\tbc\n" - "pop\taf" + "pop iy\n" + "pop\thl\n" + "pop\tde\n" + "pop\tbc\n" + "pop\taf" }, { "adjustsp", "lda sp,-(sp + %d)" }, { "profileenter", - "ld a,#3\n" - "rst\t0x28" + "ld a,#3\n" + "rst\t0x08" }, { "profileexit", - "ld a,#4\n" - "rst\t0x28" + "ld a,#4\n" + "rst\t0x08" }, { NULL, NULL } }; -static const ASM_MAPPING _rgbds_mapping[] = { - { "global", "GLOBAL %s" }, - { "extern", "GLOBAL %s" }, - { "slabeldef", "%s:" }, - { "labeldef", "%s:" }, - { "tlabeldef", ".l%05d:" }, - { "tlabel", ".l%05d" }, - { "fileprelude", - "; Generated using the rgbds tokens.\n" - "\t; We have to define these here as sdcc doesn't make them global by default\n" - "\tGLOBAL __mulschar\n" - "\tGLOBAL __muluchar\n" - "\tGLOBAL __mulint\n" - "\tGLOBAL __divschar\n" - "\tGLOBAL __divuchar\n" - "\tGLOBAL __divsint\n" - "\tGLOBAL __divuint\n" - "\tGLOBAL __modschar\n" - "\tGLOBAL __moduchar\n" - "\tGLOBAL __modsint\n" - "\tGLOBAL __moduint\n" - "\tGLOBAL __mullong\n" - "\tGLOBAL __modslong\n" - "\tGLOBAL __divslong\n" - "\tGLOBAL banked_call\n" - "\tGLOBAL banked_ret\n" - }, - { "functionheader", - "; ---------------------------------\n" - "; Function %s\n" - "; ---------------------------------" - }, - { "functionlabeldef", "%s:" }, - { "zero", "$00" }, - { "one", "$01" }, - { "area", "SECTION \"%s\",CODE" }, - { "areadata", "SECTION \"%F_%s\",BSS" }, - { "areacode", "SECTION \"%F_CODE\",%s" }, - { "areahome", "SECTION \"%F_CODE\",%s" }, - { "ascii", "DB \"%s\"" }, - { "ds", "DS %d" }, - { "db", "DB" }, - { "dbs", "DB %s" }, - { "dw", "DW" }, - { "dws", "DW %s" }, - { "immed", "" }, - { "constbyte", "$%02X" }, - { "constword", "$%04X" }, - { "immedword", "$%04X" }, - { "immedbyte", "$%02X" }, - { "hashedstr", "%s" }, - { "lsbimmeds", "%s & $FF" }, - { "msbimmeds", "%s >> 8" }, - { "bankimmeds", "BANK(%s)" }, - { "module", "; MODULE %s" }, - { NULL, NULL } -}; - -static const ASM_MAPPING _rgbds_gb_mapping[] = { - { "pusha", - "push af\n" - "\tpush bc\n" - "\tpush de\n" - "\tpush hl" - }, - { "popa", - "pop hl\n" - "\tpop de\n" - "\tpop bc\n" - "\tpop af" - }, - { "di", "di" }, - { "ei", "ei" }, - { "adjustsp", "add sp,-%d" }, - { "enter", "" }, - { "ldahli", "ld a,[hl+]" }, - { "*hl", "[hl]" }, - { "ldahlsp", "ld hl,[sp+%d]" }, - { "ldaspsp", "add sp,%d" }, - { "*pair", "[%s]" }, - { NULL, NULL } -}; - -static const ASM_MAPPING _isas_mapping[] = { - { "global", "GLOBAL %s" }, - { "extern", "GLOBAL %s" }, - { "slabeldef", "%s:" }, - { "labeldef", "%s:" }, - { "tlabeldef", "?l%05d:" }, - { "tlabel", "?l%05d" }, - { "fileprelude", - ";Generated using the isas tokens.\n" - "\tLPREFIX '?' ; Treat labels starting with ? as local.\n" - "\tONCNUM ; Numbers are hex\n" - "\tCAPSOFF ; Case sensitive\n" - "\tISDMG ; Gameboy mode\n" - "_CODE\tGROUP\n" - "\t; We have to define these here as sdcc doesnt make them global by default\n" - "\tGLOBAL __mulschar\n" - "\tGLOBAL __muluchar\n" - "\tGLOBAL __mulint\n" - "\tGLOBAL __divschar\n" - "\tGLOBAL __divuchar\n" - "\tGLOBAL __divsint\n" - "\tGLOBAL __divuint\n" - "\tGLOBAL __modschar\n" - "\tGLOBAL __moduchar\n" - "\tGLOBAL __modsint\n" - "\tGLOBAL __moduint\n" - "\tGLOBAL banked_call\n" - "\tGLOBAL banked_ret\n" - }, - { "functionheader", - "; ---------------------------------\n" - "; Function %s\n" - "; ---------------------------------" - }, - { "functionlabeldef", "%s:" }, - { "zero", "$00" }, - { "one", "$01" }, - { "area", "%s\tGROUP" }, - { "areacode", "_CODE\tGROUP" }, - { "areadata", "_DATA\tGROUP" }, - { "areahome", "_CODE\tGROUP" }, - { "ascii", "DB \"%s\"" }, - { "ds", "DS %d" }, - { "db", "DB" }, - { "dbs", "DB %s" }, - { "dw", "DW" }, - { "dws", "DW %s" }, - { "immed", "" }, - { "constbyte", "0x%02X" }, - { "constword", "0x%04X" }, - { "immedword", "0x%04X" }, - { "immedbyte", "0x%02X" }, - { "hashedstr", "%s" }, - { "lsbimmeds", "%s & 0xFF" }, - { "msbimmeds", "%s >> 8" }, - { "bankimmeds", "!%s" }, - { "module", "; MODULE %s" }, - { NULL, NULL } -}; - -static const ASM_MAPPING _isas_gb_mapping[] = { - { "pusha", - "push af\n" - "push bc\n" - "push de\n" - "push hl" - }, - { "popa", - "pop hl\n" - "pop de\n" - "pop bc\n" - "pop af" - }, - { "di", "di" }, - { "ei", "ei" }, - { "adjustsp", "add sp,-%d" }, - { "enter", "" }, - { "ldahli", "ld a,(hli)" }, - { "*hl", "(hl)" }, - { "ldahlsp", "ldhl sp,%d" }, - { "ldaspsp", "add sp,%d" }, - { "*pair", "(%s)" }, - { NULL, NULL } -}; - -static const ASM_MAPPING _z80asm_mapping[] = { - { "global", "XDEF %s" }, - { "extern", "XREF %s" }, - { "slabeldef", "\n.%s" }, - { "labeldef", "\n.%s" }, - { "tlabeldef", "\n.l%N%05d" }, - { "tlabel", "l%N%05d" }, - { "fileprelude", - "; Generated using the z80asm/z88 tokens.\n" - "\tXREF __muluchar_rrx_s\n" - "\tXREF __mulschar_rrx_s\n" - "\tXREF __mulint_rrx_s\n" - "\tXREF __mullong_rrx_s\n" - "\tXREF __divuchar_rrx_s\n" - "\tXREF __divschar_rrx_s\n" - "\tXREF __divsint_rrx_s\n" - "\tXREF __divuint_rrx_s\n" - "\tXREF __divulong_rrx_s\n" - "\tXREF __divslong_rrx_s\n" - "\tXREF __rrulong_rrx_s\n" - "\tXREF __rrslong_rrx_s\n" - "\tXREF __rlulong_rrx_s\n" - "\tXREF __rlslong_rrx_s\n" - }, - { "functionheader", - "; ---------------------------------\n" - "; Function %s\n" - "; ---------------------------------" - }, - { "functionlabeldef", ".%s" }, - { "zero", "$00" }, - { "one", "$01" }, - { "ascii", "DEFM \"%s\"" }, - { "ds", "DEFS %d" }, - { "db", "DEFB" }, - { "dbs", "DEFB %s" }, - { "dw", "DEFW" }, - { "dws", "DEFB %s" }, - { "immed", "" }, - { "constbyte", "$%02X" }, - { "constword", "$%04X" }, - { "immedword", "$%04X" }, - { "immedbyte", "$%02X" }, - { "hashedstr", "%s" }, - { "lsbimmeds", "%s ~ $FF" }, - { "msbimmeds", "%s / 256" }, - - { "bankimmeds", "BANK(%s)" }, - { "module", "MODULE %s" }, - { "area", "; Area %s" }, - { "areadata", "; Aread BSS" }, - { "areacode", "; Area CODE" }, - { "areahome", "; Area HOME" }, - { NULL, NULL } -}; - -static const ASM_MAPPING _z80asm_z80_mapping[] = { - { "*ixx", "(ix%+d)" }, - { "*iyx", "(iy%+d)" }, - { "*hl", "(hl)" }, - { "di", "di" }, - { "ei", "ei" }, - { "ldahli", - "ld a,(hl)\n" - "inc\thl" }, - { "ldahlsp", - "ld hl,%d\n" - "add\thl,sp" }, - { "ldaspsp", - "ld iy,%d\n" - "add\tiy,sp\n" - "ld\tsp,iy" }, - { "*pair", "(%s)" }, - { "enter", - "push\tix\n" - "ld\tix,0\n" - "add\tix,sp" }, - { "pusha", - "push af\n" - "push\tbc\n" - "push\tde\n" - "push\thl\n" - "push\tiy" - }, - { "popa", - "pop\tiy\n" - "pop\thl\n" - "pop\tde\n" - "pop\tbc\n" - "pop\taf" - }, - { "adjustsp", "lda sp,(sp%+d)" }, - { "profileenter", - "ld a,3\n" - "rst\t$08" - }, - { "profileexit", - "ld a,4\n" - "rst\t$08" - }, - { NULL, NULL } -}; - -static const ASM_MAPPINGS _isas = { - NULL, - _isas_mapping -}; - -const ASM_MAPPINGS _isas_gb = { - &_isas, - _isas_gb_mapping -}; - -static const ASM_MAPPINGS _rgbds = { - NULL, - _rgbds_mapping -}; - -const ASM_MAPPINGS _rgbds_gb = { - &_rgbds, - _rgbds_gb_mapping -}; - -const ASM_MAPPINGS _asxxxx_gb = { - &asm_asxxxx_mapping, - _asxxxx_gb_mapping -}; - const ASM_MAPPINGS _asxxxx_z80 = { &asm_asxxxx_mapping, _asxxxx_z80_mapping }; -static const ASM_MAPPINGS _z80asm = { - NULL, - _z80asm_mapping -}; - -const ASM_MAPPINGS _z80asm_z80 = { - &_z80asm, - _z80asm_z80_mapping -}; - -const ASM_MAPPINGS _asxxxx_r2k = { - &asm_asxxxx_mapping, - _asxxxx_r2k_mapping -}; diff --git a/src/backend/peep.c b/src/backend/peep.c index d3cfe25d3..fe20eb664 100644 --- a/src/backend/peep.c +++ b/src/backend/peep.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- peep.c - source file for peephole optimizer helper functions - Written By - Philipp Klaus Krause + Written By - Philipp Klaus Krause 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 @@ -22,10 +22,10 @@ what you give them. Help stamp out software-hoarding! -------------------------------------------------------------------------*/ -#include "SDCCglobl.h" -#include "SDCCicode.h" -#include "SDCCpeeph.h" -#include "common.h" +#include "../SDCCglobl.h" +#include "../SDCCicode.h" +#include "../SDCCpeeph.h" +#include "../common.h" #include "gen.h" #include "z80.h" @@ -34,10 +34,19 @@ werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()"); \ } while (0) -/*#define D(_s) { printf _s; fflush(stdout); }*/ +#if 0 +#define D(_s) \ + { \ + printf _s; \ + fflush(stdout); \ + } +#else #define D(_s) +#endif -#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1)) +#define ISINST(l, i) \ + (!STRNCASECMP((l), (i), sizeof(i) - 1) && \ + (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) typedef enum { S4O_CONDJMP, @@ -51,6 +60,10 @@ typedef enum { static struct { lineNode *head; } _G; +extern bool z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX + 1]; +extern bool z80_symmParm_in_calls_from_current_function; +extern bool z80_regs_preserved_in_calls_from_current_function[IYH_IDX + 1]; + /*-----------------------------------------------------------------*/ /* univisitLines - clear "visited" flag in all lines */ /*-----------------------------------------------------------------*/ @@ -86,7 +99,10 @@ static bool isReturned(const char *what) { if (sym->type->select.d.dcl_type != FUNCTION) NOTUSEDERROR(); spec = &(sym->etype->select.s); - if (spec->noun == V_VOID) + if (spec->noun == V_VOID || + spec->noun == V_INT && + spec->b_longlong) // long long is not returned via registers for the + // Z80-related ports size = 0; else if (spec->noun == V_CHAR || spec->noun == V_BOOL) size = 1; @@ -106,18 +122,18 @@ static bool isReturned(const char *what) { size = 4; } - switch (*what) { - case 'd': - return (size >= 4); - case 'e': - return (size >= 3); - case 'h': - return (size >= 2); - case 'l': - return (size >= 1); - default: - return FALSE; - } + switch (*what) { + case 'd': + return (size >= 4); + case 'e': + return (size >= 3); + case 'h': + return (size >= 2); + case 'l': + return (size >= 1); + default: + return FALSE; + } } /*-----------------------------------------------------------------*/ @@ -146,12 +162,12 @@ static lineNode *findLabel(const lineNode *pl) { /* 1. extract label in opcode */ - /* In each mcs51 jumping opcode the label is at the end of the opcode */ + /* In each z80 jumping opcode the label is at the end of the opcode */ p = strlen(pl->line) - 1 + pl->line; /* scan backward until ',' or '\t' */ for (; p > pl->line; p--) - if (*p == ',' || *p == '\t') + if (*p == ',' || isspace(*p)) break; /* sanity check */ @@ -178,7 +194,147 @@ static lineNode *findLabel(const lineNode *pl) { /* Check if reading arg implies reading what. */ static bool argCont(const char *arg, const char *what) { - return (arg[0] == '#') ? FALSE : StrStr(arg, what) != NULL; + wassert(arg); + + while (isspace(*arg) || *arg == ',') + arg++; + + if (arg[0] == '#' || arg[0] == '_') + return false; + + if (arg[0] == '(' && arg[1] && arg[2] && (arg[2] != ')' && arg[3] != ')')) + return false; + + if (*arg == '(') + arg++; + + if (arg[0] == '#' || arg[0] == '_') + return false; + + // Get suitable end to avoid reading into further arguments. + const char *end = strchr(arg, ','); + if (!end) + end = arg + strlen(arg); + + const char *found = StrStr(arg, what); + + return (found && found < end); +} + +static bool z80MightBeParmInCallFromCurrentFunction(const char *what) { + if (strchr(what, 'l') && + z80_regs_used_as_parms_in_calls_from_current_function[L_IDX]) + return TRUE; + if (strchr(what, 'h') && + z80_regs_used_as_parms_in_calls_from_current_function[H_IDX]) + return TRUE; + if (strchr(what, 'e') && + z80_regs_used_as_parms_in_calls_from_current_function[E_IDX]) + return TRUE; + if (strchr(what, 'd') && + z80_regs_used_as_parms_in_calls_from_current_function[D_IDX]) + return TRUE; + if (strchr(what, 'c') && + z80_regs_used_as_parms_in_calls_from_current_function[C_IDX]) + return TRUE; + if (strchr(what, 'b') && + z80_regs_used_as_parms_in_calls_from_current_function[B_IDX]) + return TRUE; + if (strstr(what, "iy") && + (z80_regs_used_as_parms_in_calls_from_current_function[IYL_IDX] || + z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX])) + return TRUE; + + return FALSE; +} + +/* Check if the flag implies reading what. */ +static bool z80MightReadFlagCondition(const char *cond, const char *what) { + while (isspace(*cond)) + cond++; + + if (!STRNCASECMP(cond, "po", 2) || !STRNCASECMP(cond, "pe", 2)) + return !strcmp(what, "pf"); + if (tolower(cond[0]) == 'm' || tolower(cond[0]) == 'p') + return !strcmp(what, "sf"); + + // skip inverted conditions + if (tolower(cond[0]) == 'n') + cond++; + + if (tolower(cond[0]) == 'c') + return !strcmp(what, "cf"); + if (tolower(cond[0]) == 'z') + return !strcmp(what, "zf"); + return true; +} + +static bool z80MightReadFlag(const lineNode *pl, const char *what) { + if (ISINST(pl->line, "ld") || ISINST(pl->line, "or") || + ISINST(pl->line, "cp") || ISINST(pl->line, "di") || + ISINST(pl->line, "ei") || ISINST(pl->line, "im") || + ISINST(pl->line, "in")) + return false; + if (ISINST(pl->line, "nop") || ISINST(pl->line, "add") || + ISINST(pl->line, "sub") || ISINST(pl->line, "and") || + ISINST(pl->line, "xor") || ISINST(pl->line, "dec") || + ISINST(pl->line, "inc") || ISINST(pl->line, "cpl") || + ISINST(pl->line, "bit") || ISINST(pl->line, "res") || + ISINST(pl->line, "set") || ISINST(pl->line, "pop") || + ISINST(pl->line, "rlc") || ISINST(pl->line, "rrc") || + ISINST(pl->line, "sla") || ISINST(pl->line, "sra") || + ISINST(pl->line, "srl") || ISINST(pl->line, "scf") || + ISINST(pl->line, "cpd") || ISINST(pl->line, "cpi") || + ISINST(pl->line, "ind") || ISINST(pl->line, "ini") || + ISINST(pl->line, "ldd") || ISINST(pl->line, "ldi") || + ISINST(pl->line, "neg") || ISINST(pl->line, "rld") || + ISINST(pl->line, "rrd")) + return false; + if (ISINST(pl->line, "halt") || ISINST(pl->line, "rlca") || + ISINST(pl->line, "rrca") || ISINST(pl->line, "cpdr") || + ISINST(pl->line, "cpir") || ISINST(pl->line, "indr") || + ISINST(pl->line, "inir") || ISINST(pl->line, "lddr") || + ISINST(pl->line, "ldir") || ISINST(pl->line, "outd") || + ISINST(pl->line, "outi") || ISINST(pl->line, "jdnz")) + return false; + + if (ISINST(pl->line, "rl") || ISINST(pl->line, "rr") || + ISINST(pl->line, "rla") || ISINST(pl->line, "rra") || + ISINST(pl->line, "sbc") || ISINST(pl->line, "adc") || + ISINST(pl->line, "ccf")) + return (!strcmp(what, "cf")); + + if (ISINST(pl->line, "daa")) + return (!strcmp(what, "cf") || !strcmp(what, "nf") || !strcmp(what, "hf")); + + if (ISINST(pl->line, "push")) + return (argCont(pl->line + 4, "af")); + + if (ISINST(pl->line, "ex")) + return (argCont(pl->line + 2, "af")); + + // catch c, nc, z, nz, po, pe, p and m + if (ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) + return (strchr(pl->line, ',') && + z80MightReadFlagCondition(pl->line + 2, what)); + + // flags don't matter according to calling convention + if (ISINST(pl->line, "reti") || ISINST(pl->line, "retn")) + return false; + + if (ISINST(pl->line, "call")) + return (strchr(pl->line, ',') && + z80MightReadFlagCondition(pl->line + 4, what)); + + if (ISINST(pl->line, "ret")) + return (pl->line[3] == '\t' && + z80MightReadFlagCondition(pl->line + 3, what)); + + // we don't know anything about this + if (ISINST(pl->line, "rst")) + return true; + + return true; } static bool z80MightRead(const lineNode *pl, const char *what) { @@ -187,70 +343,152 @@ static bool z80MightRead(const lineNode *pl, const char *what) { if (strcmp(what, "ixl") == 0 || strcmp(what, "ixh") == 0) what = "ix"; - if (strcmp(pl->line, "call\t__initrleblock") == 0) + if (ISINST(pl->line, "call") && strcmp(what, "sp") == 0) + return TRUE; + + if (strcmp(pl->line, "call\t__initrleblock") == 0 && + (strchr(what, 'd') != 0 || strchr(what, 'e') != 0)) return TRUE; - if (strcmp(pl->line, "call\t__sdcc_call_hl") == 0 && + if (strcmp(pl->line, "call\t___sdcc_call_hl") == 0 && (strchr(what, 'h') != 0 || strchr(what, 'l') != 0)) return TRUE; - if (strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0) - return FALSE; + if (strcmp(pl->line, "call\t___sdcc_call_iy") == 0 && strstr(what, "iy") != 0) + return TRUE; - if (ISINST(pl->line, "ret")) - return (isReturned(what)); + if (strncmp(pl->line, "call\t___sdcc_bcall_", 19) == 0) + if (strchr(what, pl->line[19]) != 0 || strchr(what, pl->line[20]) != 0 || + strchr(what, pl->line[21]) != 0) + return TRUE; + + if (ISINST(pl->line, "call") && strchr(pl->line, ',') == 0) { + const symbol *f = findSym(SymbolTab, 0, pl->line + 6); + if (f) { + const value *args = FUNC_ARGS(f->type); + + if (IFFUNC_ISZ88DK_FASTCALL(f->type) && + args) // Has one register argument of size up to 32 bit. + { + const unsigned int size = getSize(args->type); + wassert(!args->next); // Only one argment allowed in __z88dk_fastcall + // functions. + if (strchr(what, 'l') && size >= 1) + return TRUE; + if (strchr(what, 'h') && size >= 2) + return TRUE; + if (strchr(what, 'e') && size >= 3) + return TRUE; + if (strchr(what, 'd') && size >= 4) + return TRUE; + } + return FALSE; + } else // Fallback needed for calls through function pointers and for calls + // to literal addresses. + return z80MightBeParmInCallFromCurrentFunction(what); + } + + if (ISINST(pl->line, "reti") || ISINST(pl->line, "retn")) + return (strcmp(what, "sp") == 0); + + if (ISINST(pl->line, "ret")) // --reserve-regs-iy uses ret in code gen for + // calls through function pointers + return (IY_RESERVED ? isReturned(what) || + z80MightBeParmInCallFromCurrentFunction(what) + : isReturned(what)) || + strcmp(what, "sp") == 0; if (!strcmp(pl->line, "ex\t(sp), hl") || !strcmp(pl->line, "ex\t(sp),hl")) - return (!strchr(what, 'h') || !strchr(what, 'l')); + return (!strcmp(what, "h") || !strcmp(what, "l") || + strcmp(what, "sp") == 0); + if (!strcmp(pl->line, "ex\t(sp), ix") || !strcmp(pl->line, "ex\t(sp),ix")) + return (!!strstr(what, "ix") || strcmp(what, "sp") == 0); + if (!strcmp(pl->line, "ex\t(sp), iy") || !strcmp(pl->line, "ex\t(sp),iy")) + return (!!strstr(what, "iy") || strcmp(what, "sp") == 0); if (!strcmp(pl->line, "ex\tde, hl") || !strcmp(pl->line, "ex\tde,hl")) - return (!strchr(what, 'h') || !strchr(what, 'l') || !strchr(what, 'd') || - !strchr(what, 'e')); - if (ISINST(pl->line, "ld\t")) { - if (strstr(strchr(pl->line, ','), what) && - strchr(pl->line, ',')[1] != '#' && - !(strchr(pl->line, ',')[1] == '(' && strchr(pl->line, ',')[2] == '#') && - !(strchr(pl->line, ',')[1] == '(' && strchr(pl->line, ',')[3] != ')' && - strchr(pl->line, ',')[4] != ')')) - return TRUE; + return (!strcmp(what, "h") || !strcmp(what, "l") || !strcmp(what, "d") || + !strcmp(what, "e")); + if (ISINST(pl->line, "ld")) { + if (argCont(strchr(pl->line, ','), what)) + return (true); if (*(strchr(pl->line, ',') - 1) == ')' && strstr(pl->line + 3, what) && (strchr(pl->line, '#') == 0 || strchr(pl->line, '#') > strchr(pl->line, ','))) - return TRUE; - if (strcmp(what, "ix") || - strcmp(what, - "iy")) /* SirCmpwn Note: it's probably going to be used if this - check passes, but I'm not trying very hard */ - return TRUE; - return FALSE; + return (true); + return (false); } - if (!strcmp(pl->line, "xor\ta, a") || !strcmp(pl->line, "xor\ta,a")) - return FALSE; - - if (ISINST(pl->line, "adc\t") || ISINST(pl->line, "add\t") || - ISINST(pl->line, "and\t") || ISINST(pl->line, "sbc\t") || - ISINST(pl->line, "sub\t") || ISINST(pl->line, "xor\t")) { - return (argCont(pl->line + 4, what)); + // ld a, #0x00 + if ((ISINST(pl->line, "xor") || ISINST(pl->line, "sub")) && + (!strcmp(pl->line + 4, "a, a") || !strcmp(pl->line + 4, "a,a") || + (!strchr(pl->line, ',') && !strcmp(pl->line + 4, "a")))) + return (false); + + // ld a, #0x00 + if (!strcmp(pl->line, "and\ta, #0x00") || !strcmp(pl->line, "and\ta,#0x00") || + !strcmp(pl->line, "and\t#0x00")) + return (false); + + // ld a, #0xff + if (!strcmp(pl->line, "or\ta, #0xff") || !strcmp(pl->line, "or\ta,#0xff") || + !strcmp(pl->line, "or\t#0xff")) + return (false); + + if (ISINST(pl->line, "adc") || ISINST(pl->line, "add") || + ISINST(pl->line, "and") || ISINST(pl->line, "sbc") || + ISINST(pl->line, "sub") || ISINST(pl->line, "xor")) { + const char *arg = pl->line + 4; + while (isspace(*arg)) + arg++; + if (arg[0] == 'a' && arg[1] == ',') { + if (!strcmp(what, "a")) + return (true); + arg += 2; + } else if (!strncmp(arg, "hl", 2) && arg[2] == ',') // add hl, rr + { + if (!strcmp(what, "h") || !strcmp(what, "l")) + return (true); + arg += 3; + } else if (!strncmp(arg, "sp", 2) && arg[2] == ',') // add sp, rr + { + if (!strcmp(what, "sp")) + return (true); + arg += 3; + } else if (arg[0] == 'i') // add ix/y, rr + { + if (!strncmp(arg, what, 2)) + return (true); + arg += 3; + } + return (argCont(arg, what)); } - if (ISINST(pl->line, "or\t") || ISINST(pl->line, "cp\t")) { - if (argCont(pl->line + 3, what)) - return TRUE; - if (strcmp("a", what) == 0) - return TRUE; - return FALSE; + if (ISINST(pl->line, "or") || ISINST(pl->line, "cp")) { + const char *arg = pl->line + 3; + while (isspace(*arg)) + arg++; + if (*arg == 'a' && *(arg + 1) == ',') { + if (!strcmp(what, "a")) + return (true); + arg += 2; + } else if (!strncmp(arg, "hl", 2) && *(arg + 2) == ',') { + if (!strcmp(what, "h") || !strcmp(what, "l")) + return (true); + arg += 3; + } + return (argCont(arg, what)); } if (ISINST(pl->line, "neg")) return (strcmp(what, "a") == 0); - if (ISINST(pl->line, "pop\t")) - return FALSE; + if (ISINST(pl->line, "pop")) + return (strcmp(what, "sp") == 0); - if (ISINST(pl->line, "push\t")) - return (strstr(pl->line + 5, what) != 0); + if (ISINST(pl->line, "push")) + return (strstr(pl->line + 5, what) != 0 || strcmp(what, "sp") == 0); - if (ISINST(pl->line, "dec\t") || ISINST(pl->line, "inc\t")) { + if (ISINST(pl->line, "dec") || ISINST(pl->line, "inc")) { return (argCont(pl->line + 4, what)); } @@ -258,119 +496,282 @@ static bool z80MightRead(const lineNode *pl, const char *what) { return (!strcmp(what, "a")); if (ISINST(pl->line, "di") || ISINST(pl->line, "ei")) - return (FALSE); + return (false); - // Rotate and shift group (todo: rld rrd, maybe sll) + // Rotate and shift group if (ISINST(pl->line, "rlca") || ISINST(pl->line, "rla") || - ISINST(pl->line, "rrca") || ISINST(pl->line, "rra")) { + ISINST(pl->line, "rrca") || ISINST(pl->line, "rra") || + ISINST(pl->line, "daa")) { return (strcmp(what, "a") == 0); } - if (ISINST(pl->line, "rl\t") || ISINST(pl->line, "rr\t")) { + if (ISINST(pl->line, "rl") || ISINST(pl->line, "rr")) { return (argCont(pl->line + 3, what)); } - if (ISINST(pl->line, "rlc\t") || ISINST(pl->line, "sla\t") || - ISINST(pl->line, "sra\t") || ISINST(pl->line, "srl\t")) { + if (ISINST(pl->line, "rlc") || ISINST(pl->line, "sla") || + ISINST(pl->line, "rrc") || ISINST(pl->line, "sra") || + ISINST(pl->line, "srl")) { return (argCont(pl->line + 4, what)); } + if ((ISINST(pl->line, "rld") || ISINST(pl->line, "rrd"))) + return (!!strstr("ahl", what)); // Bit set, reset and test group - if (ISINST(pl->line, "bit\t") || ISINST(pl->line, "set\t") || - ISINST(pl->line, "res\t")) { - return (argCont(pl->line + 4, what)); + if (ISINST(pl->line, "bit") || ISINST(pl->line, "set") || + ISINST(pl->line, "res")) { + return (argCont(strchr(pl->line + 4, ','), what)); } - if (ISINST(pl->line, "ccf")) - return FALSE; + if (ISINST(pl->line, "ccf") || ISINST(pl->line, "scf") || + ISINST(pl->line, "nop") || ISINST(pl->line, "halt")) + return (false); - if (strncmp(pl->line, "jp\t", 3) == 0 || strncmp(pl->line, "jr\t", 3) == 0) - return FALSE; + if (ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) + return (false); - if (ISINST(pl->line, "djnz\t")) + if (ISINST(pl->line, "djnz")) return (strchr(what, 'b') != 0); - if (!IS_GB && ISINST(pl->line, "ldir")) - return (!strcmp(what, "b") || !strcmp(what, "c") || !strcmp(what, "d") || - !strcmp(what, "e") || !strcmp(what, "h") || !strcmp(what, "l")); + if ((ISINST(pl->line, "ldd") || ISINST(pl->line, "lddr") || + ISINST(pl->line, "ldi") || ISINST(pl->line, "ldir"))) + return (strchr("bcdehl", *what)); + if ((ISINST(pl->line, "cpd") || ISINST(pl->line, "cpdr") || + ISINST(pl->line, "cpi") || ISINST(pl->line, "cpir"))) + return (strchr("abchl", *what)); - /* TODO: There are out and in variants that do not read bc. */ - if (!IS_GB && ISINST(pl->line, "out\t")) + if (ISINST(pl->line, "out")) return (strstr(strchr(pl->line + 4, ','), what) != 0 || - !strcmp(what, "b") || !strcmp(what, "c")); - if (!IS_GB && ISINST(pl->line, "in\t")) - return (strstr(pl->line + 3, what) != 0 || !strcmp(what, "b") || - !strcmp(what, "c")); - - if (IS_Z180 && ISINST(pl->line, "mlt\t")) + strstr(pl->line + 4, "(c)") && + (!strcmp(what, "b") || !strcmp(what, "c"))); + if (ISINST(pl->line, "in")) + return (!strstr(strchr(pl->line + 4, ','), "(c)") && !strcmp(what, "a") || + strstr(strchr(pl->line + 4, ','), "(c)") && + (!strcmp(what, "b") || !strcmp(what, "c"))); + + if ((ISINST(pl->line, "ini") || ISINST(pl->line, "ind") || + ISINST(pl->line, "inir") || ISINST(pl->line, "indr") || + ISINST(pl->line, "outi") || ISINST(pl->line, "outd") || + ISINST(pl->line, "otir") || ISINST(pl->line, "otdr"))) + return (strchr("bchl", *what)); + + if ((IS_EZ80_Z80) && ISINST(pl->line, "in0")) + return (false); + + if ((IS_EZ80_Z80) && ISINST(pl->line, "mlt")) return (argCont(pl->line + 4, what)); - if (IS_Z180 && ISINST(pl->line, "tst\t")) + if ((IS_EZ80_Z80) && + (ISINST(pl->line, "otim") || ISINST(pl->line, "otimr") || + ISINST(pl->line, "otir") || ISINST(pl->line, "otirx"))) + return (strchr("bchl", *what)); + + if ((IS_EZ80_Z80) && ISINST(pl->line, "slp")) + return (false); + + if ((IS_EZ80_Z80) && ISINST(pl->line, "tst")) return (argCont(pl->line + 4, what)); - if (IS_RAB && ISINST(pl->line, "mul")) - return (!strcmp(what, "b") || !strcmp(what, "c") || !strcmp(what, "d") || - !strcmp(what, "e")); + if ((IS_EZ80_Z80) && ISINST(pl->line, "tstio")) + return (!strcmp(what, "c")); + + if (IS_EZ80_Z80 && ISINST(pl->line, "lea")) + return (argCont(strchr(pl->line + 4, ','), what)); - if (IS_RAB && ISINST(pl->line, "bool\t")) - return (argCont(pl->line + 5, what)); + if (IS_EZ80_Z80 && ISINST(pl->line, "pea")) + return (argCont(pl->line + 4, what) || !strcmp(what, "sp")); /* TODO: Can we know anything about rst? */ if (ISINST(pl->line, "rst")) - return (TRUE); + return (true); - return TRUE; + return (true); } static bool z80UncondJump(const lineNode *pl) { - if ((ISINST(pl->line, "jp\t") || ISINST(pl->line, "jr\t")) && + if ((ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) && strchr(pl->line, ',') == 0) return TRUE; return FALSE; } static bool z80CondJump(const lineNode *pl) { - if (((ISINST(pl->line, "jp\t") || ISINST(pl->line, "jr\t")) && + if (((ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) && strchr(pl->line, ',') != 0) || - ISINST(pl->line, "djnz\t")) + ISINST(pl->line, "djnz")) return TRUE; return FALSE; } +// TODO: z80 flags only partly implemented +static bool z80SurelyWritesFlag(const lineNode *pl, const char *what) { + /* LD instruction is never change flags except LD A,I and LD A,R. + But it is most popular instruction so place it first */ + if (ISINST(pl->line, "ld")) { + if (!!strcmp(what, "pf") || !argCont(pl->line + 3, "a")) + return false; + const char *p = strchr(pl->line + 4, ','); + if (p == NULL) + return false; /* unknown instruction */ + ++p; + return argCont(p, "i") || argCont(p, "r"); + } + + if (ISINST(pl->line, "rlca") || ISINST(pl->line, "rrca") || + ISINST(pl->line, "rra") || ISINST(pl->line, "rla")) + return (!!strcmp(what, "zf") && !!strcmp(what, "sf") && + !!strcmp(what, "pf")); + + if (ISINST(pl->line, "adc") || ISINST(pl->line, "and") || + ISINST(pl->line, "sbc") || ISINST(pl->line, "sub") || + ISINST(pl->line, "xor") || ISINST(pl->line, "and") || + ISINST(pl->line, "rlc") || ISINST(pl->line, "rrc") || + ISINST(pl->line, "sla") || ISINST(pl->line, "sra") || + ISINST(pl->line, "srl") || ISINST(pl->line, "neg")) + return true; + + if (ISINST(pl->line, "or") || ISINST(pl->line, "cp") || + ISINST(pl->line, "rl") || ISINST(pl->line, "rr")) + return true; + + if (ISINST(pl->line, "bit") || ISINST(pl->line, "cpd") || + ISINST(pl->line, "cpi") || ISINST(pl->line, "ind") || + ISINST(pl->line, "ini") || ISINST(pl->line, "rrd")) + return (!!strcmp(what, "cf")); + + if (ISINST(pl->line, "cpdr") || ISINST(pl->line, "cpir") || + ISINST(pl->line, "indr") || ISINST(pl->line, "inir") || + ISINST(pl->line, "otdr") || ISINST(pl->line, "otir") || + ISINST(pl->line, "outd") || ISINST(pl->line, "outi")) + return (!!strcmp(what, "cf")); + + if (ISINST(pl->line, "daa")) + return (!!strcmp(what, "nf")); + + if (ISINST(pl->line, "scf") || ISINST(pl->line, "ccf")) + return (!!strcmp(what, "zf") && !!strcmp(what, "sf") && + !!strcmp(what, "pf")); + + if (ISINST(pl->line, "cpl")) + return (!!strcmp(what, "zf") && !!strcmp(what, "cf")); + + // only for simple registers + if ((ISINST(pl->line, "inc") || ISINST(pl->line, "dec")) && + (strlen(pl->line + 4) == 1)) + return (!!strcmp(what, "cf")); + + if (ISINST(pl->line, "add")) + return ( + argCont(pl->line + 4, "a") || + (!!strcmp(what, "zf") && !!strcmp(what, "sf") && !!strcmp(what, "pf"))); + + // pop af writes + if (ISINST(pl->line, "pop")) + return (argCont(pl->line + 4, "af")); + + // according to calling convention caller has to save flags + if (ISINST(pl->line, "ret") || ISINST(pl->line, "call")) + return true; + + /* handle IN0 r,(n) and IN r,(c) instructions */ + if (ISINST(pl->line, "in0") || + (!strncmp(pl->line, "in\t", 3) && + (!strcmp(pl->line + 5, "(c)") || !strcmp(pl->line + 5, "(bc)")))) + return (!!strcmp(what, "cf")); + + return false; +} + static bool z80SurelyWrites(const lineNode *pl, const char *what) { if (strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0) what = "iy"; if (strcmp(what, "ixl") == 0 || strcmp(what, "ixh") == 0) what = "ix"; - if (ISINST(pl->line, "xor\t") && strcmp(what, "a") == 0) - return TRUE; - if (ISINST(pl->line, "ld\t") && strncmp(pl->line + 3, "hl", 2) == 0 && + // ld a, #0x00 + if ((ISINST(pl->line, "xor") || ISINST(pl->line, "sub")) && + !strcmp(what, "a") && + (!strcmp(pl->line + 4, "a, a") || !strcmp(pl->line + 4, "a,a") || + (!strchr(pl->line, ',') && !strcmp(pl->line + 4, "a")))) + return (true); + + // ld a, #0x00 + if (!strcmp(what, "a") && + (!strcmp(pl->line, "and\ta, #0x00") || + !strcmp(pl->line, "and\ta,#0x00") || !strcmp(pl->line, "and\t#0x00"))) + return (true); + + // ld a, #0xff + if (!strcmp(what, "a") && + (!strcmp(pl->line, "or\ta, #0xff") || !strcmp(pl->line, "or\ta,#0xff") || + !strcmp(pl->line, "or\t#0xff"))) + return (true); + + if (ISINST(pl->line, "ld") && strncmp(pl->line + 3, "hl", 2) == 0 && (what[0] == 'h' || what[0] == 'l')) - return TRUE; - if (ISINST(pl->line, "ld\t") && strncmp(pl->line + 3, "de", 2) == 0 && + return (true); + if (ISINST(pl->line, "ld") && strncmp(pl->line + 3, "de", 2) == 0 && (what[0] == 'd' || what[0] == 'e')) - return TRUE; - if (ISINST(pl->line, "ld\t") && strncmp(pl->line + 3, "bc", 2) == 0 && + return (true); + if (ISINST(pl->line, "ld") && strncmp(pl->line + 3, "bc", 2) == 0 && (what[0] == 'b' || what[0] == 'c')) - return TRUE; - if (ISINST(pl->line, "ld\t") && + return (true); + if ((ISINST(pl->line, "ld") || ISINST(pl->line, "in")) && strncmp(pl->line + 3, what, strlen(what)) == 0 && pl->line[3 + strlen(what)] == ',') - return TRUE; - if (ISINST(pl->line, "pop\t") && strstr(pl->line + 4, what)) - return TRUE; - if (ISINST(pl->line, "call\t") && strchr(pl->line, ',') == 0 && - strcmp(what, "ix")) - return TRUE; + return (true); + + if (ISINST(pl->line, "pop") && strstr(pl->line + 4, what)) + return (true); + if (ISINST(pl->line, "call") && strchr(pl->line, ',') == 0) { + const symbol *f = findSym(SymbolTab, 0, pl->line + 6); + const bool *preserved_regs; + + if (!strcmp(what, "ix")) + return (false); + + if (f) + preserved_regs = f->type->funcAttrs.preserved_regs; + else // Err on the safe side. + preserved_regs = z80_regs_preserved_in_calls_from_current_function; + + if (!strcmp(what, "c")) + return !preserved_regs[C_IDX]; + if (!strcmp(what, "b")) + return !preserved_regs[B_IDX]; + if (!strcmp(what, "e")) + return !preserved_regs[E_IDX]; + if (!strcmp(what, "d")) + return !preserved_regs[D_IDX]; + if (!strcmp(what, "l")) + return !preserved_regs[L_IDX]; + if (!strcmp(what, "h")) + return !preserved_regs[H_IDX]; + if (!strcmp(what, "iy")) + return !preserved_regs[IYL_IDX] && !preserved_regs[IYH_IDX]; + } if (strcmp(pl->line, "ret") == 0) - return TRUE; - if (ISINST(pl->line, "ld\tiy") && strncmp(what, "iy", 2) == 0) - return TRUE; + return true; - return FALSE; + if (IS_EZ80_Z80) + if (ISINST(pl->line, "mlt")) + return (strchr(pl->line + 4, *what) != 0); + + if (IS_EZ80_Z80) { + if (ISINST(pl->line, "otim") || ISINST(pl->line, "otimr") || + ISINST(pl->line, "otdm") || ISINST(pl->line, "otdmr")) + return (strchr("bchl", *what) != NULL); + + if (ISINST(pl->line, "in0")) + return (!strncmp(pl->line + 4, what, strlen(what))); + } + + if (IS_EZ80_Z80 && ISINST(pl->line, "lea")) + return (strstr(pl->line + 4, what)); + + return (false); } static bool z80SurelyReturns(const lineNode *pl) { - if (strcmp(pl->line, "\tret") == 0) + if (strcmp(pl->line, "ret") == 0) return TRUE; return FALSE; } @@ -413,6 +814,7 @@ static bool z80SurelyReturns(const lineNode *pl) { /*-----------------------------------------------------------------*/ static S4O_RET scan4op(lineNode **pl, const char *what, const char *untilOp, lineNode **plCond) { + bool isFlag = (strlen(what) == 2 && what[1] == 'f'); for (; *pl; *pl = (*pl)->next) { if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) continue; @@ -431,9 +833,16 @@ static S4O_RET scan4op(lineNode **pl, const char *what, const char *untilOp, (*pl)->visited = TRUE; - if (z80MightRead(*pl, what)) { - D(("S4O_RD_OP\n")); - return S4O_RD_OP; + if (isFlag) { + if (z80MightReadFlag(*pl, what)) { + D(("S4O_RD_OP (flag)\n")); + return S4O_RD_OP; + } + } else { + if (z80MightRead(*pl, what)) { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } } if (z80UncondJump(*pl)) { @@ -453,9 +862,16 @@ static S4O_RET scan4op(lineNode **pl, const char *what, const char *untilOp, return S4O_CONDJMP; } - if (z80SurelyWrites(*pl, what)) { - D(("S4O_WR_OP\n")); - return S4O_WR_OP; + if (isFlag) { + if (z80SurelyWritesFlag(*pl, what)) { + D(("S4O_WR_OP (flag)\n")); + return S4O_WR_OP; + } + } else { + if (z80SurelyWrites(*pl, what)) { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } } /* Don't need to check for de, hl since z80MightRead() does that */ @@ -536,8 +952,6 @@ static bool isRegPair(const char *what) { return TRUE; if (strcmp(what, "hl") == 0) return TRUE; - if (strcmp(what, "sp") == 0) - return TRUE; if (strcmp(what, "ix") == 0) return TRUE; if (strcmp(what, "iy") == 0) @@ -550,26 +964,43 @@ static bool isRegPair(const char *what) { bool z80notUsed(const char *what, lineNode *endPl, lineNode *head) { lineNode *pl; D(("Checking for %s\n", what)); + + if (strcmp(what, "af") == 0) { + if (!z80notUsed("a", endPl, head)) + return FALSE; + what++; + } + + if (strcmp(what, "f") == 0) + return z80notUsed("zf", endPl, head) && z80notUsed("cf", endPl, head) && + z80notUsed("sf", endPl, head) && z80notUsed("pf", endPl, head) && + z80notUsed("nf", endPl, head) && z80notUsed("hf", endPl, head); + + if (strcmp(what, "iy") == 0) { + if (IY_RESERVED) + return FALSE; + return (z80notUsed("iyl", endPl, head) && z80notUsed("iyh", endPl, head)); + } + + if (strcmp(what, "ix") == 0) + return (z80notUsed("ixl", endPl, head) && z80notUsed("ixh", endPl, head)); + if (isRegPair(what)) { char low[2], high[2]; low[0] = what[1]; high[0] = what[0]; low[1] = 0; high[1] = 0; - if (strcmp(what, "iy") == 0) { - if (IY_RESERVED) - return FALSE; - return (z80notUsed("iyl", endPl, head) && z80notUsed("iyh", endPl, head)); - } - if (strcmp(what, "ix") == 0) { - if (IY_RESERVED) - return FALSE; - return (z80notUsed("ixl", endPl, head) && z80notUsed("ixh", endPl, head)); - } return (z80notUsed(low, endPl, head) && z80notUsed(high, endPl, head)); } - if (!isReg(what) && !isUReg(what)) + // P/V and L/V (rarbbits) are the same flag + if (!strcmp(what, "vf") || !strcmp(what, "lf")) + what = "pf"; + + // enable sp and flags + if (!isReg(what) && !isUReg(what) && strcmp(what, "sp") && + strcmp(what + 1, "f")) return FALSE; _G.head = head; @@ -586,13 +1017,13 @@ bool z80notUsed(const char *what, lineNode *endPl, lineNode *head) { bool z80notUsedFrom(const char *what, const char *label, lineNode *head) { lineNode *cpl; - for (cpl = _G.head; cpl; cpl = cpl->next) { + for (cpl = head; cpl; cpl = cpl->next) { if (cpl->isLabel && !strncmp(label, cpl->line, strlen(label))) { return z80notUsed(what, cpl, head); } } - return FALSE; + return false; } bool z80canAssign(const char *op1, const char *op2, const char *exotic) { @@ -602,10 +1033,11 @@ bool z80canAssign(const char *op1, const char *op2, const char *exotic) { // or immediate. if (exotic) { if (!strcmp(exotic, "ix") || !strcmp(exotic, "iy")) { - if (isReg(op1)) + if (isReg(op1) || (IS_EZ80_Z80 && isRegPair(op1))) return TRUE; } else if (!strcmp(op2, "ix") || !strcmp(op2, "iy")) { - if (isReg(exotic) || exotic[0] == '#') + if (isReg(exotic) || exotic[0] == '#' || + (IS_EZ80_Z80 && isRegPair(exotic))) return TRUE; } @@ -621,38 +1053,110 @@ bool z80canAssign(const char *op1, const char *op2, const char *exotic) { return TRUE; // Same if at most one of them is (hl). - if (isReg(dst) && !strcmp(src, "(hl)")) + if ((isReg(dst) || (IS_EZ80_Z80 && isRegPair(dst))) && !strcmp(src, "(hl)")) return TRUE; - if (!strcmp(dst, "(hl)") && isReg(src)) + if (!strcmp(dst, "(hl)") && (isReg(src) || (IS_EZ80_Z80 && isRegPair(src)))) return TRUE; - // Can assign between a and (bc), (de) - if (!strcmp(dst, "a") && (!strcmp(src, "(bc)") || !strcmp(src, "(de)"))) + // Can assign between a and (bc), (de), (hl+), (hl-) + if (!strcmp(dst, "a") && (!strcmp(src, "(bc)") || !strcmp(src, "(de)") || + !strcmp(src, "(hl+)") || !strcmp(src, "(hl-)"))) return TRUE; - if ((!strcmp(dst, "(bc)") || !strcmp(dst, "(de)")) && !strcmp(src, "a")) + if ((!strcmp(dst, "(bc)") || !strcmp(dst, "(de)") || !strcmp(src, "(hl+)") || + !strcmp(src, "(hl-)")) && + !strcmp(src, "a")) return TRUE; // Can load immediate values directly into registers and register pairs. - if ((isReg(dst) || isRegPair(dst)) && src[0] == '#') + if ((isReg(dst) || isRegPair(dst) || !strcmp(src, "sp")) && src[0] == '#') return TRUE; - if ((!strcmp(dst, "a") || isRegPair(dst)) && !strncmp(src, "(#", 2)) + if ((!strcmp(dst, "a") || + ((isRegPair(dst) || !strcmp(src, "sp")))) && + !strncmp(src, "(#", 2)) return TRUE; - if (!strncmp(dst, "(#", 2) && (!strcmp(src, "a") || isRegPair(src))) + if (!strncmp(dst, "(#", 2) && + (!strcmp(src, "a") || (isRegPair(src)) || !strcmp(src, "sp"))) return TRUE; // Can load immediate values directly into (hl). if (!strcmp(dst, "(hl)") && src[0] == '#') return TRUE; + // Can load hl into sp + if (!strcmp(dst, "sp") && !strcmp(src, "hl")) + return TRUE; + return FALSE; } +static const char *registerBaseName(const char *op) { + if (!strcmp(op, "d") || !strcmp(op, "e") || !strcmp(op, "(de)")) + return "de"; + if (!strcmp(op, "b") || !strcmp(op, "c") || !strcmp(op, "(bc)")) + return "bc"; + if (!strcmp(op, "h") || !strcmp(op, "l") || !strcmp(op, "(hl)") || + !strcmp(op, "(hl+)") || !strcmp(op, "(hl-)")) + return "hl"; + if (!strcmp(op, "iyh") || !strcmp(op, "iyl") || strstr(op, "iy")) + return "iy"; + if (!strcmp(op, "ixh") || !strcmp(op, "ixl") || strstr(op, "ix")) + return "ix"; + if (!strcmp(op, "a")) + return "af"; + return op; +} + +// canJoinRegs(reg_hi reg_lo [dst]) returns TRUE, +bool z80canJoinRegs(const char **regs, char dst[20]) { + // check for only 2 source registers + if (!regs[0] || !regs[1] || regs[2]) + return FALSE; + size_t l1 = strlen(regs[0]); + size_t l2 = strlen(regs[1]); + if (l1 + l2 >= 20) + return FALSE; + if (l1 == 0 || l2 == 0) { + if (l1 == 0 && l2 == 0) + return FALSE; + strcpy(dst, registerBaseName(regs[l1 ? 0 : 1])); + } else { + memcpy(&dst[0], regs[0], l1); + memcpy(&dst[l1], regs[1], l2 + 1); // copy including \0 + } + if (!strcmp(dst, "ixhixl") || !strcmp(dst, "iyhiyl")) { + dst[2] = '\0'; + } + return isRegPair(dst); +} + +bool z80canSplitReg(const char *reg, char dst[][16], int nDst) { + int i; + if (nDst < 0 || nDst > 2) + return FALSE; + if (!strcmp(reg, "bc") || !strcmp(reg, "de") || !strcmp(reg, "hl")) { + for (i = 0; i < nDst; ++i) { + dst[i][0] = reg[i]; + dst[i][1] = '\0'; + } + } else if ((!strcmp(reg, "ix") || !strcmp(reg, "iy"))) { + for (i = 0; i < nDst; ++i) { + dst[i][0] = reg[0]; + dst[i][1] = reg[1]; + dst[i][2] = "hl"[i]; + dst[i][3] = '\0'; + } + } else + return FALSE; + + return TRUE; +} + int z80instructionSize(lineNode *pl) { const char *op1start, *op2start; /* move to the first operand: - * leading spaces are already removed, skip the mnenonic */ + * leading spaces are already removed, skip the mnemonic */ for (op1start = pl->line; *op1start && !isspace(*op1start); ++op1start) ; @@ -678,24 +1182,35 @@ int z80instructionSize(lineNode *pl) { op2start = NULL; /* All ld instructions */ - if (ISINST(pl->line, "ld\t") || ISINST(pl->line, "ld ")) { - /* These 3 are the only cases of 4 byte long ld instructions. */ + if (ISINST(pl->line, "ld")) { + /* These 4 are the only cases of 4 byte long ld instructions. */ if (!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2)) return (4); if ((argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) && op2start[0] == '#') return (4); + if (op1start[0] == '(' && STRNCASECMP(op1start, "(bc)", 4) && - STRNCASECMP(op1start, "(de)", 4) && STRNCASECMP(op1start, "(hl)", 4) && - STRNCASECMP(op2start, "hl", 2) && STRNCASECMP(op2start, "a", 1)) + STRNCASECMP(op1start, "(de)", 4) && + STRNCASECMP(op1start, "(hl", 3) && STRNCASECMP(op2start, "hl", 2) && + STRNCASECMP(op2start, "a", 1) && + (STRNCASECMP(op2start, "sp", 2)) || + op2start[0] == '(' && STRNCASECMP(op2start, "(bc)", 4) && + STRNCASECMP(op1start, "(de)", 4) && + STRNCASECMP(op2start, "(hl", 3) && STRNCASECMP(op1start, "hl", 2) && + STRNCASECMP(op1start, "a", 1)) return (4); - if (IS_RAB && !STRNCASECMP(op1start, "hl", 2) && - (argCont(op2start, "(hl)") || argCont(op2start, "(iy)"))) - return (4); - if (IS_RAB && !STRNCASECMP(op1start, "hl", 2) && - (argCont(op2start, "(sp)") || argCont(op2start, "(ix)"))) - return (3); + /* Rabbit 16-bit pointer load */ + if (IS_EZ80_Z80 && /* eZ80 16-bit pointer load */ + (!STRNCASECMP(op1start, "bc", 2) || !STRNCASECMP(op1start, "de", 2) || + !STRNCASECMP(op1start, "hl", 2) || !STRNCASECMP(op1start, "ix", 2) || + !STRNCASECMP(op1start, "iy", 2))) { + if (!STRNCASECMP(op2start, "(hl)", 4)) + return (2); + if (argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) + return (3); + } /* These 4 are the only remaining cases of 3 byte long ld instructions. */ if (argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) @@ -703,10 +1218,9 @@ int z80instructionSize(lineNode *pl) { if (argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) return (3); if ((op1start[0] == '(' && STRNCASECMP(op1start, "(bc)", 4) && - STRNCASECMP(op1start, "(de)", 4) && - STRNCASECMP(op1start, "(hl)", 4)) || + STRNCASECMP(op1start, "(de)", 4) && STRNCASECMP(op1start, "(hl", 3)) || (op2start[0] == '(' && STRNCASECMP(op2start, "(bc)", 4) && - STRNCASECMP(op2start, "(de)", 4) && STRNCASECMP(op2start, "(hl)", 4))) + STRNCASECMP(op2start, "(de)", 4) && STRNCASECMP(op2start, "(hl", 3))) return (3); if (op2start[0] == '#' && (!STRNCASECMP(op1start, "bc", 2) || !STRNCASECMP(op1start, "de", 2) || @@ -731,29 +1245,33 @@ int z80instructionSize(lineNode *pl) { return (1); if (ISINST(pl->line, "ex")) { if (!op2start) { - werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, __func__, - 4, pl->line); + werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, + __FUNCTION__, 4, pl->line); return (4); } if (argCont(op1start, "(sp)") && - (IS_RAB || !STRNCASECMP(op2start, "ix", 2) || + (!STRNCASECMP(op2start, "ix", 2) || !STRNCASECMP(op2start, "iy", 2))) return (2); return (1); } /* Push / pop */ + if (ISINST(pl->line, "push") && op1start[0] == '#') + return (4); if (ISINST(pl->line, "push") || ISINST(pl->line, "pop")) { if (!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2)) return (2); return (1); } - /* 16 bit add / subtract / and */ + /* 16 bit add / subtract / and / or */ if ((ISINST(pl->line, "add") || ISINST(pl->line, "adc") || - ISINST(pl->line, "sbc") || IS_RAB && ISINST(pl->line, "and")) && + ISINST(pl->line, "sbc") || + false && (ISINST(pl->line, "and") || ISINST(pl->line, "or"))) && !STRNCASECMP(op1start, "hl", 2)) { - if (ISINST(pl->line, "add") || ISINST(pl->line, "and")) + if (ISINST(pl->line, "add") || ISINST(pl->line, "and") || + ISINST(pl->line, "or")) return (1); return (2); } @@ -761,15 +1279,6 @@ int z80instructionSize(lineNode *pl) { (!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2))) return (2); - /* signed 8 bit adjustment to stack pointer */ - if ((IS_RAB || IS_GB) && ISINST(pl->line, "add") && - !STRNCASECMP(op1start, "sp", 2)) - return (2); - - /* 16 bit adjustment to stack pointer */ - if (IS_TLCS90 && ISINST(pl->line, "add") && !STRNCASECMP(op1start, "sp", 2)) - return (3); - /* 8 bit arithmetic, two operands */ if (op2start && op1start[0] == 'a' && (ISINST(pl->line, "add") || ISINST(pl->line, "adc") || @@ -811,7 +1320,7 @@ int z80instructionSize(lineNode *pl) { /* Bit */ if (ISINST(pl->line, "bit") || ISINST(pl->line, "set") || ISINST(pl->line, "res")) { - if (argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) + if (argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) return (4); return (2); } @@ -827,15 +1336,11 @@ int z80instructionSize(lineNode *pl) { return (3); } - if (IS_RAB && (ISINST(pl->line, "ipset3") || ISINST(pl->line, "ipset2") || - ISINST(pl->line, "ipset1") || ISINST(pl->line, "ipset0") || - ISINST(pl->line, "ipres"))) - return (2); - - if (ISINST(pl->line, "reti") || ISINST(pl->line, "retn")) + if ((ISINST(pl->line, "reti") || ISINST(pl->line, "retn"))) return (2); - if (ISINST(pl->line, "ret") || ISINST(pl->line, "rst")) + if (ISINST(pl->line, "ret") || ISINST(pl->line, "reti") || + ISINST(pl->line, "rst")) return (1); if (ISINST(pl->line, "call")) @@ -861,43 +1366,47 @@ int z80instructionSize(lineNode *pl) { ISINST(pl->line, "ot")) return (2); - if (ISINST(pl->line, "di") || ISINST(pl->line, "ei")) - return (1); + if (( IS_EZ80_Z80) && + (ISINST(pl->line, "in0") || ISINST(pl->line, "out0"))) + return (3); - if (IS_Z180 && ISINST(pl->line, "mlt")) + if ((IS_EZ80_Z80) && ISINST(pl->line, "mlt")) return (2); - if (IS_Z180 && ISINST(pl->line, "tst")) + if ((IS_EZ80_Z80) && ISINST(pl->line, "tst")) return ((op1start[0] == '#' || op2start && op1start[0] == '#') ? 3 : 2); - if (IS_RAB && ISINST(pl->line, "mul")) - return (1); - if (ISINST(pl->line, "lddr") || ISINST(pl->line, "ldir")) return (2); - if (IS_R3KA && (ISINST(pl->line, "lddsr") || ISINST(pl->line, "ldisr") || - ISINST(pl->line, "lsdr") || ISINST(pl->line, "lsir") || - ISINST(pl->line, "lsddr") || ISINST(pl->line, "lsidr"))) - return (2); - - if (IS_R3KA && (ISINST(pl->line, "uma") || ISINST(pl->line, "ums"))) - return (2); - - if (IS_RAB && ISINST(pl->line, "bool")) - return (!STRNCASECMP(op1start, "hl", 2) ? 1 : 2); + if (IS_EZ80_Z80 && (ISINST(pl->line, "lea") || ISINST(pl->line, "pea"))) + return (3); - if (ISINST(pl->line, ".db")) { + if (ISINST(pl->line, ".db") || ISINST(pl->line, ".byte")) { int i, j; for (i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++) ; return (i); } + if (ISINST(pl->line, ".dw") || ISINST(pl->line, ".word")) { + int i, j; + for (i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++) + ; + return (i * 2); + } + /* If the instruction is unrecognized, we shouldn't try to optimize. */ /* For all we know it might be some .ds or similar possibly long line */ /* Return a large value to discourage optimization. */ - werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, __func__, 999, - pl->line); + if (pl->ic) + werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, __func__, + 999, pl->line); + else + werrorfl("unknown", 0, W_UNRECOGNIZED_ASM, __func__, 999, pl->line); return (999); } + +bool z80symmParmStack(void) { + return z80_symmParm_in_calls_from_current_function; +} diff --git a/src/backend/peep.h b/src/backend/peep.h index 7e62cc781..ce2860b45 100644 --- a/src/backend/peep.h +++ b/src/backend/peep.h @@ -25,4 +25,7 @@ bool z80notUsed(const char *what, lineNode *endPl, lineNode *head); bool z80notUsedFrom(const char *what, const char *label, lineNode *head); bool z80canAssign(const char *dst, const char *src, const char *exotic); +bool z80symmParmStack(void); +bool z80canJoinRegs(const char **regs, char dst[20]); +bool z80canSplitReg(const char *reg, char dst[][16], int nDst); int z80instructionSize(lineNode *node); diff --git a/src/backend/peeph-z80.def b/src/backend/peeph-z80.def index fd45b8d16..411bcdf96 100644 --- a/src/backend/peeph-z80.def +++ b/src/backend/peeph-z80.def @@ -3,7 +3,7 @@ // Some of these peepholes could be potentially moved to peeph.def, but a // GBZ80 expert should have a look at them before. // -// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2012 +// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2020 // // 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 @@ -20,1994 +20,9 @@ // Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. replace restart { - ld %1, %1 -} by { - ; peephole 0 removed redundant load from %1 into %1. -} if notVolatile(%1) - -replace restart { - ld %1, %2 -} by { - ; peephole 1 removed dead load from %2 into %1. -} if notVolatile(%1), notUsed(%1), notVolatile(%2) - -replace restart { - add ix,sp -} by { - ; peephole 1a removed dead frame pointer setup. -} if notUsed('ix') - -replace restart { - ld %1, %2 + %3 -} by { - ; peephole 2 removed dead load from %2 + %3 into %1. -} if notVolatile(%1), notUsed(%1) -// Should probably check for notVolatile(%2), too, but gives many false positives and no regression tests fail. - -replace restart { - ld %1, (iy) -} by { - ld %1, (iy + 0) - ; peephole 3 made 0 offset explicit. -} - -replace restart { - ld (iy), %1 -} by { - ld (iy + 0), %1 - ; peephole 4 made 0 offset explicit. -} - -replace restart { - inc hl -} by { - ; peephole 5 removed dead increment of hl. -} if notUsed('hl') - -replace restart { - dec hl -} by { - ; peephole 6 removed dead decrement of hl. -} if notUsed('hl') - -replace restart { - ld %1, (iy + %2) -} by { - ; peephole 7 removed dead load from %2 (iy) into %1. -} if notUsed(%1) -// Should probably check for notVolatile(), but gives many false positives and no regression tests fail. - -replace restart { - ld %1, (ix + %2) -} by { - ; peephole 8 removed dead load from %2 (ix) into %1. -} if notUsed(%1) - -replace restart { - ld %1, %2 - ld %3, %1 -} by { - ; peephole 9 loaded %3 from %2 directly instead of going through %1. - ld %3, %2 -} if canAssign(%3 %2), notVolatile(%1), notUsed(%1) - -replace restart { - ld %1, %2 - ld %3, %4 - ld %5, %1 -} by { - ld %5, %2 - ; peephole 10 loaded %5 from %2 directly instead of going through %1. - ld %3, %4 -} if canAssign(%5 %2), notVolatile(%1), operandsNotRelated(%1 %4), operandsNotRelated(%1 %3), operandsNotRelated(%4 %5), notUsed(%1), notSame(%3 %4 '(hl)' '(de)' '(bc)'), notVolatile(%5) -// Rule OK unless both %5 and %4 are volatile, but we can't express that directly. - -replace restart { - ld %1, %2 (%3) - ld %4, %1 -} by { - ; peephole 11 loaded %2 (%3) into %4 directly instead of going through %1. - ld %4, %2 (%3) -} if canAssign(%4 %2 %3), notVolatile(%1), notUsed(%1) - -replace restart { - ld %1, %2 - ld %3 (%4), %1 -} by { - ; peephole 12 loaded %2 into %3 (%4) directly instead of going through %1. - ld %3 (%4), %2 -} if canAssign(%3 %4 %2), notVolatile(%1), notUsed(%1) - -replace restart { - ld %1, %2 (%3) - ld %4, %5 (%6) - ld %7, %1 -} by { - ld %7, %2 (%3) - ; peephole 13 loaded %2 (%3) into %7 directly instead of going through %1. - ld %4, %5 (%6) -} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), notSame(%1 %4), notSame(%7 %4) - -replace restart { - ld %1, %7 - ld %5 (%6), %4 - ld %2 (%3), %1 -} by { - ld %5 (%6), %4 - ; peephole 14 loaded %7 into %2 (%3) directly instead of going through %1. - ld %2 (%3), %7 -} if canAssign(%2 %3 %7), notVolatile(%1), notUsed(%1), notSame(%1 %4) - -replace restart { - ld %1, %2 (%3) - ld %4, %5 - ld %7, %1 -} by { - ld %7, %2 (%3) - ; peephole 15 loaded %2 (%3) into %7 directly instead of going through %1. - ld %4, %5 -} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), notSame(%1 %5), notSame(%7 %4), notSame(%7 %5), notSame(%4 '(hl)' '(de)' '(bc)'), notSame(%5 '(hl)' '(de)' '(bc)' '(iy)') - -replace restart { - ld %1,#%2 - ld a,%3 (%1) -} by { - ; peephole 16 loaded %2 into a directly instead of going through %1. - ld a,(#%2 + %3) -} if notUsed(%1) - -replace restart { - ld hl,#%1 - ld a,(hl) -} by { - ld a,(#%1) - ; peephole 17 loaded a from (#%1) directly instead of using hl. -} if notUsed('hl') - -replace restart { - ld hl,#%1 + %2 - ld a,(hl) -} by { - ; peephole 18 loaded %2 into a directly instead of using hl. - ld a,(#%1 + %2) -} if notUsed('hl') - -replace restart { - ld hl,#%1 - ld (hl),a -} by { - ld (#%1),a - ; peephole 19 loaded (#%1) from a directly instead of using hl. -} if notUsed('hl') - -replace restart { - ld hl,#%1 + %2 - ld (hl),a -} by { - ld (#%1 + %2),a - ; peephole 20 loaded (#%1) from a directly instead of using hl. -} if notUsed('hl') - -replace restart { - srl %1 - ld a,%1 -} by { - ld a,%1 - ; peephole 21 shifted in a instead of %1. - srl a -} if notVolatile(%1), notUsed(%1) - -replace restart { - ld e, l - ld d, h - ld a, (de) - srl a - ld (de), a -} by { - ld e, l - ld d, h - srl (hl) - ld a, (hl) - ; peephole 21a shifted in (hl) instead of a. -} - -replace restart { - ld a, %1 (%2) - srl a - ld %1 (%2), a -} by { - srl %1 (%2) - ld a, %1 (%2) - ; peephole 21b shifted in (%2) instead of a. -} - -replace restart { - ld iy, #%1 - ld %2, (iy + %3) - srl %2 - bit %4, (iy + %3) -} by { - ld hl, #%1 + %3 - ; peephole 21c used hl instead of iy. - ld %2, (hl) - srl %2 - bit %4, (hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld %1,(hl) - ld a,%2 (%3) - sub a,%1 -} by { - ld a,%2 (%3) - ; peephole 22 used (hl) in sub directly instead of going through %1. - sub a,(hl) -} if notVolatile(%1), notUsed(%1) - -replace restart { - inc bc - ld l,c - ld h,b -} by { - ld l,c - ld h,b - ; peephole 23 incremented in hl instead of bc. - inc hl -} if notUsed('bc') - -replace restart { - inc de - ld l,e - ld h,d -} by { - ld l,e - ld h,d - ; peephole 24 incremented in hl instead of de. - inc hl -} if notUsed('de') - -replace restart { - ld c,l - ld b,h - ld a,#%1 - ld (bc),a -} by { - ld c,l - ld b,h - ld (hl),#%1 - ; peephole 25 loaded #%1 into (hl) instead of (bc). -} - -replace restart { - ex de, hl - push de -} by { - ; peephole 26 pushed hl directly instead of going through de. - push hl -} if notUsed('de'), notUsed('hl') - -replace restart { - ld l,%1 - ld h,d - push hl -} by { - ; peephole 27 pushed de instead of hl removing a load. - ld e,%1 - push de -} if notUsed('hl'), notUsed('e') - -replace restart { - ex de, hl - push bc - push de -} by { - ; peephole 28 pushed hl directly instead of going through de. - push bc - push hl -} if notUsed('de'), notUsed('hl') - -replace restart { - ld l,c - ld h,b - push hl -} by { - ; peephole 29 pushed bc directly instead of going through hl. - push bc -} if notUsed('hl') - -replace restart { - ld l,%1 - ld h,b - push hl -} by { - ; peephole 30 pushed bc instead of hl removing a load. - ld c,%1 - push bc -} if notUsed('hl'), notUsed('c') - -replace restart { - ld c,l - ld b,h - push %1 - push bc -} by { - ; peephole 31 pushed hl directly instead of going through bc. - push %1 - push hl -} if notUsed('bc'), notSame(%1 'bc') - -replace restart { - pop de - ld l, e - ld h, d -} by { - ; peephole 32 popped hl directly instead of going through de. - pop hl -} if notUsed('de') - -replace restart { - pop bc - ld l, c - ld h, b -} by { - ; peephole 33 popped hl directly instead of going through bc. - pop hl -} if notUsed('bc') - -replace restart { - ld (ix + %1), %2 - ld %3, (ix + %1) -} by { - ; peephole 34 loaded %3 from %2 instead of going through %1 (ix). - ld (ix + %1), %2 - ld %3, %2 -} -// Don't need to check for volatile, since ix is used for the stack. - -replace restart { - ld (ix + %1), a - push de - ld %2, (ix + %1) -} by { - ld (ix + %1), a - push de - ; peephole 34a loaded %2 from a instead of %1 (ix) - ld %2, a -} - -replace restart { - push af - inc sp - ld a,e - push af - inc sp -} by { - ; peephole 35 pushed de instead of pushing a twice. - ld d,a - push de -} if notUsed('d'), notUsed('a') - -replace restart { - push af - inc sp - ld a,#%1 - push af - inc sp -} by { - ; peephole 36 pushed de instead of pushing a twice. - ld d,a - ld e,#%1 - push de -} if notUsed('de') - -replace restart { - push af - inc sp - ld a,#%1 - push af - inc sp -} by { - ; peephole 37 pushed bc instead of pushing a twice. - ld b,a - ld c,#%1 - push bc -} if notUsed('bc') - -replace restart { - push bc - inc sp - push de - inc sp -} by { - ld c, d - ; peephole 37a combined pushing of b and d. - push bc -} if notUsed('c') - -replace restart { - push bc - inc sp - ld a, c - push af - inc sp -} by { - push bc - ld a, c - ; peephole 38 simplified pushing bc. -} - -replace restart { - push de - inc sp - ld a, #%1 - push af - inc sp -} by { - ld e, #%1 - push de - ; peephole 39 simplified pushing de. -} if notUsed('e') - -replace restart { - ld a,#%1 - ld d,a -} by { - ; peephole 40 loaded #%1 into d directly instead of going through a. - ld d,#%1 -} if notUsed('a') - -replace restart { - ld %1,a - ld %2,%1 -} by { - ; peephole 41 loaded %2 from a directly instead of going through %1. - ld %2,a -} if notUsed(%1) - -replace restart { - ld a,%1 (%3) - push af - inc sp - ld a,%2 (%3) - push af - inc sp -} by { - ; peephole 42 pushed %1 (%3), %2(%3) through hl instead of af. - ld h,%1 (%3) - ld l,%2 (%3) - push hl -} if notUsed('a'), notUsed('hl') - -replace restart { - ld c, l - ld b, h - push bc -} by { - ; peephole 43 pushed hl instead of bc. - push hl -} if notUsed('bc') - -replace restart { - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - push hl -} by { - ; peephole 43a pushed bc instead of hl. - ld c, (hl) - inc hl - ld b, (hl) - push bc -} if notUsed('bc'), notUsed('hl') - -replace restart { - pop %1 - push %1 -} by { - ; peephole 44 eleminated dead pop/push pair. -} if notUsed(%1) - -replace restart { - push hl - pop bc -} by { - ld c, l - ld b, h - ; peephole 44a replaced push/pop pair by loads. -} if notUsed('hl') - -replace restart { - push hl - pop de -} by { - ld e, l - ld d, h - ; peephole 44b replaced push/pop pair by loads. -} if notUsed('hl') - -replace restart { - ld iy,#%1 - or a,(iy + %2) -} by { - ; peephole 45 used hl instead of iy. - ld hl,#%1 + %2 - or a,(hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy,#%1 - ld %2,(iy + %3) -} by { - ; peephole 46 used hl instead of iy. - ld hl,#%1 + %3 - ld %2, (hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy,#%1 - ld h,(iy + %3) -} by { - ; peephole 46a used hl instead of iy. - ld hl,#%1 + %3 - ld h, (hl) -} if notUsed('iy'), notUsed('l') - -replace restart { - ld iy,#%1 - ld (iy + %2), %3 -} by { - ; peephole 46b used hl instead of iy. - ld hl,#%1 + %2 - ld (hl), %3 -} if notUsed('iy'), notUsed('hl'), notSame(%3 'h' 'l') - -replace restart { - ld iy,#%1 - ld %2,(iy + 0) - ld %3,(iy + 1) -} by { - ; peephole 47 used hl instead of iy. - ld hl,#%1 - ld %2, (hl) - inc hl - ld %3, (hl) -} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%2 'hl') - -replace restart { - ld iy,#%1 - ld (iy + %2),%3 - ld l,(iy + %2) -} by { - ; peephole 48 used hl instead of iy. - ld hl,#%1 + %2 - ld (hl),%3 - ld l,(hl) -} if notUsed('iy'), notUsed('h') - -replace restart { - ld iy,#%1 - ld %2 (%3), %4 -} by { - ; peephole 49 used hl instead of iy. - ld hl,#%1 + %2 - ld (hl), %4 -} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%4 'hl') - -replace restart { - ld iy,#%1 - bit %2,(iy + %3) -} by { - ; peephole 49a used hl instead of iy. - ld hl,#%1+%3 - bit %2, (hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - ld %2, (iy + %3) -} by { - ; peephole 49b used hl instead of iy. - ld hl, #%1+%3 - add hl, sp - ld %2, (hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - ld %2, (iy + 0) - ld %3, (iy + 1) -} by { - ; peephole 49c used hl instead of iy. - ld hl, #%1 - add hl, sp - ld %2, (hl) - inc hl - ld %3, (hl) -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - ld l, (iy + 0) - ld h, (iy + 1) -} by { - ; peephole 49d used hl instead of iy. - ld hl, #%1 - add hl, sp - ld a, (hl) - inc hl - ld h, (hl) - ld l, a -} if notUsed('iy'), notUsed('a') - -replace restart { - ld iy, #%1 - add iy, sp - ld (iy + 0), #%2 - ld (iy + 1), #%3 -} by { - ; peephole 49e used hl instead of iy. - ld hl, #%1 - add hl, sp - ld (hl), #%2 - inc hl - ld (hl), #%3 -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1+1 - ld a, (iy + 0) - or a, (iy + 1) -} by { - ld hl, #%1 - ld a, (hl) - dec hl - or a, (hl) - ; peephole 49f used hl instead of iy. -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - ld a, (iy + 0) - or a, (iy + 1) -} by { - ld hl, #%1+1 - add hl, sp - ld a, (hl) - dec hl - or a, (hl) - ; peephole 49f' used hl instead of iy. -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - bit %2, (iy + %3) -} by { - ld hl, #%1+%3 - add hl, sp - bit %2, (hl) - ; peephole 49g used hl instead of iy. -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld iy, #%1 - add iy, sp - or a, (iy + %2) -} by { - ld hl, #%1+%2 - add hl, sp - or a, (hl) - ; peephole 49h used hl instead of iy. -} if notUsed('iy'), notUsed('hl') - -replace restart { - ld c,l - ld b,h - inc bc -} by { - ; peephole 51 incremented in hl instead of bc. - inc hl - ld c,l - ld b,h -} if notUsed('hl') - -replace restart { - ld iy, #%1 - inc (iy + %2) -} by { - ; peephole 51a incremented in (hl) instead of %2 (iy). - ld hl, #%1+%2 - inc (hl) -} if notUsed('hl'), notUsed('iy') - -replace restart { - ld iy, #%1 - dec (iy + %2) -} by { - ; peephole 51a' decremented in (hl) instead of %2 (iy). - ld hl, #%1+%2 - dec (hl) -} if notUsed('hl'), notUsed('iy') - -replace restart { - add hl, hl - ld e, l - ld d, h - inc de - inc de -} by { - inc hl - ; peephole 51b incremented once in hl instead of incrementing in de twice. - add hl, hl - ld e, l - ld d, h -} if notUsed('hl') - -replace restart { - add hl, hl - inc hl - inc hl -} by { - inc hl - ; peephole 51c incremented once in hl instead of incrementing in hl twice. - add hl, hl -} - -replace restart { - add a, a - add a, a - add a, #0x04 -} by { - inc a - ; peephole 51d incremented a once instead of adding #0x04 to a. - add a, a - add a, a -} - -replace restart { - add hl, hl - pop de - inc hl - inc hl -} by { - inc hl - ; peephole 51e incremented once in hl instead of incrementing in hl twice. - add hl, hl - pop de -} - -replace restart { - ld a,%1 (%2) - bit %3,a -} by { - ; peephole 52 tested bit of %1 (%2) directly instead of going through a. - bit %3,%1 (%2) -} if notUsed('a') - -replace restart { - ld a,%1 - bit %2,a -} by { - ; peephole 53 tested bit %2 of %1 directly instead of going through a. - bit %2,%1 -} if notUsed('a'), canAssign(%1 'b') - -replace restart { - ld a, %1 - set %2, a - ld %1, a -} by { - ; peephole 54 set bit %2 of %1 directly instead of going through a. - set %2, %1 - ld a, %1 -} if canAssign(%1 'b') -// canAssign(%1 'b') is true, iff set b, %1 is possible. - -replace restart { - ld a, %1 (%2) - set %3, a - ld %1 (%2), a -} by { - ; peephole 55 set bit %3 of %1 (%2) directly instead of going through a. - set %3, %1 (%2) - ld a, %1 (%2) -} - -replace restart { - ld a, %1 - res %2, a - ld %1, a -} by { - ; peephole 56 reset bit %2 of %1 directly instead of going through a. - res %2, %1 - ld a, %1 -} if canAssign(%1 'b') -// canAssign(%1 'b') is true, iff set b, %1 is possible. - -replace restart { - ld a, %1 (%2) - res %3, a - ld %1 (%2), a -} by { - ; peephole 57 reset bit %3 of %1 (%2) directly instead of going through a. - res %3, %1 (%2) - ld a, %1 (%2) -} - -replace restart { - ld c, %1 (%2) - ld b, %3 (%4) - ld l,c - ld h,b -} by { - ; peephole 58 stored %1 (%2) %3 (%4) into hl directly instead of going through bc. - ld l, %1 (%2) - ld h, %3 (%4) -} if notUsed('bc') - -replace restart { - ld c, %1 - ld b, %2 - ld l,c - ld h,b -} by { - ; peephole 59 stored %2%1 into hl directly instead of going through bc. - ld l, %1 - ld h, %2 -} if notUsed('bc'), operandsNotRelated(%2 'l') - -replace restart { - jp NC,%1 - jp %2 -%1: -} by { - jp C,%2 - ; peephole 60 removed jp by using inverse jump logic -%1: -} if labelRefCountChange(%1 -1) - -replace restart { - jp C,%1 - jp %2 -%1: -} by { - jp NC,%2 - ; peephole 61 removed jp by using inverse jump logic -%1: -} if labelRefCountChange(%1 -1) - -replace restart { - jp NZ,%1 - jp %2 -%1: -} by { - jp Z,%2 - ; peephole 62 removed jp by using inverse jump logic -%1: -} if labelRefCountChange(%1 -1) - -replace restart { - jp Z,%1 - jp %2 -%1: -} by { - jp NZ,%2 - ; peephole 63 removed jp by using inverse jump logic -%1: -} if labelRefCountChange(%1 -1) - -replace restart { - jp %5 -} by { - jp %6 - ; peephole 64 jumped to %6 directly instead of via %5. -} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) - -replace restart { - jp %1,%5 -} by { - jp %1,%6 - ; peephole 65 jumped to %6 directly instead of via %5. -} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) - -replace restart { - jp %1 -%2: -%1: -} by { - ; peephole 65a eliminated jump. -%2: -%1: -} if labelRefCountChange(%1 -1) - -// A peephole that makes the code longer. Let's hope it's worth it in speed gain and further optimization potential. -replace restart { - ld a,#0x00 -%1: - bit %2,a - jp Z,%3 -} by { - ld a,#0x00 - jp %3 - ; peephole 65a jumped directly to %3 instead of testing a first. -%1: - bit %2,a - jp Z,%3 -} if labelRefCountChange(%3 +1) - -replace restart { - ld %1, %2 - jp %3 - jp %4 -} by { - ld %1, %2 - jp %3 - ; peephole 65b removed unreachable jump to %3. -} - -replace restart { - ld %1, %2 - jp %3 -%3: -} by { - ld %1, %2 -%3: - ; peephole 65c removed redundant jump to %3. -} if labelRefCountChange(%3 -1) - -replace restart { - ld %1, #0x01 - bit 0, %1 - jp Z, %2 -} by { - ld %1, #0x01 - ; peephole 65d removed impossible jump to %2. -} if labelRefCountChange(%2 -1) - -replace restart { - xor a,a - ld a,#0x00 -} by { - xor a,a - ; peephole 66 removed redundant load of 0 into a. -} - -replace { - ld e,#0x%1 - ld d,#0x%2 -} by { - ld de,#0x%2%1 - ; peephole 67 combined constant loads into register pair. -} - -replace { - ld d,#0x%1 - ld e,#0x%2 -} by { - ld de,#0x%1%2 - ; peephole 67a combined constant loads into register pair. -} - -replace { - ld l,#0x%1 - ld h,#0x%2 -} by { - ld hl,#0x%2%1 - ; peephole 68 combined constant loads into register pair. -} - -replace { - ld h,#0x%1 - ld l,#0x%2 -} by { - ld hl,#0x%1%2 - ; peephole 68a combined constant loads into register pair. -} - -replace { - ld c,#0x%1 - ld b,#0x%2 -} by { - ld bc,#0x%2%1 - ; peephole 69 combined constant loads into register pair. -} - -replace { - ld b,#0x%1 - ld c,#0x%2 -} by { - ld bc,#0x%1%2 - ; peephole 69a combined constant loads into register pair. -} - -replace restart { - ld %1,a - ld a,%1 -} by { - ld %1,a - ; peephole 70 removed redundant load from %1 into a. -} if notVolatile(%1) -// This gives many false negatives and without the test no problems are encountered in the regression tests -// Maybe we can try this after 2.7.0 release - -replace restart { - ld a,%1 - ld %1,a -} by { - ld a,%1 - ; peephole 71 removed redundant load from a into %1. -} if notVolatile(%1) -// This gives many false negatives and without the test no problems are encountered in the regression tests -// Maybe we can try this after 2.7.0 release - -replace restart { - ld %1,a - ld a,%2 - or a,%1 -} by { - ld %1,a - or a,%2 - ; peephole 72 removed load by reordering or arguments. -} if notVolatile(%1), canAssign('b' %2) -// canAssign('b' %2) is true, iff or a,%2 is possible. - -replace restart { - or a,%1 - or a,a -} by { - or a,%1 - ; peephole 73 removed redundant or after or. -} - -replace restart { - or a,%1 (%2) - or a,a -} by { - or a,%1 (%2) - ; peephole 74 removed redundant or after or. -} - -replace restart { - and a,%1 - or a,a -} by { - and a,%1 - ; peephole 75 removed redundant or after and. -} - -replace restart { - xor a,%1 - or a,a -} by { - xor a,%1 - ; peephole 76 removed redundant or after xor. -} - -replace restart { - xor a,%1 (%2) - or a,a -} by { - xor a,%1 (%2) - ; peephole 77 removed redundant or after xor. -} - -replace { - ld %1,%2 - ld a,%2 -} by { - ld a,%2 - ld %1,a - ; peephole 78 load value in a first and use it next -} if notVolatile(%1 %2) - -replace restart { - ld %1,%2 - ld %3,%4 - ld %2,%1 - ld %4,%3 -} by { - ld %1,%2 - ld %3,%4 - ; peephole 79 removed redundant load from %3%1 into %4%2 -} if notVolatile(%1 %2 %3 %4) - -replace restart { - push de - inc sp - ld a,e - push af - inc sp -} by { - push de - ; peephole 80 pushed de -} if notUsed('a') - -replace restart { - ld iy,%1 - add iy,sp - ld sp,iy -} by { - ld hl,%1 - add hl,sp - ld sp,hl - ; peephole 81 fixed stack using hl instead of iy. -} if notUsed('hl'), notUsed('iy') - -replace restart { - ld a,%1 - sub a,%2 - jp %3,%4 - ld a,%1 -} by { - ld a,%1 - cp a,%2 - jp %3,%4 - ; peephole 82 removed load from %1 into a by replacing sub with cp - assert a=%1 -} if notVolatile(%1), notUsedFrom(%4 'a') - -replace restart { - assert a=%1 - sub a,%2 - jp %3,%4 - ld a,%1 -} by { - cp a,%2 - jp %3,%4 - ; peephole 83 removed load from %1 into a by replacing sub with cp - assert a=%1 -} if notUsedFrom(%4 'a') - -replace restart { - assert a=%1 -} by { -} - -replace restart { - sub a,#0xFF - jp Z,%1 -} by { - inc a - ; peephole 84 replaced sub a,#0xFF by inc a. - jp Z,%1 -} - -replace restart { - sub a,#0xFF - jp NZ,%1 -} by { - inc a - ; peephole 85 replaced sub a,#0xFF by inc a. - jp NZ,%1 -} - -replace restart { - rlca - ld a,#0x00 - rla -} by { - rlca - and a,#0x01 - ; peephole 86 replaced zero load, rla by and since rlca writes the same value to carry bit and least significant bit. -} - -replace restart { - ld %1,%2 - push %1 - pop %4 - ld %1,%3 -} by { - ld %4,%2 - ; peephole 87 moved %2 directly into de instead of going through %1. - ld %1,%3 -} - -replace restart { - add a,#0x00 - ld %2,a - ld a,%3 - adc a,%4 -} by { - ; peephole 88 removed lower part of multibyte addition. - ld %2,a - ld a,%3 - add a,%4 -} - -replace restart { - ld a, l - add a, #0x%1 - ld e, a - ld a, h - adc a, #0x%2 - ld d, a -} by { - ld de, #0x%2%1 - add hl, de - ; peephole 89 used 16-bit addition. - ld e, l - ld d, h - ld a, h -} if notUsed('hl') - -replace restart { - ld a, l - add a, #0x%1 - ld c, a - ld a, h - adc a, #0x%2 - ld b, a -} by { - ld bc, #0x%2%1 - add hl,bc - ; peephole 90 used 16-bit addition. - ld c, l - ld b, h - ld a, h -} if notUsed('hl') - -replace restart { - ld %1,a - ld a,%2 - add a,%1 -} by { - ; peephole 91 removed loads by exploiting commutativity of addition. - add a,%2 -} if notVolatile(%1), notUsed(%1), canAssign('b' %2) -// canAssign('b' %2) is true, iff add a,%2 is possible. - -replace restart { - ld (ix + %1),a - ld a,#%2 - add a,(ix + %1) -} by { - ld (ix + %1),a - ; peephole 92 removed loads by exploiting commutativity of addition. - add a,#%2 -} -// Don't need to check for volatile, since ix is used to access the stack. - -replace restart { - ld l,(ix + %1) - ld h,(ix + %2) - ld a,(hl) - inc a - ld l,(ix + %1) - ld h,(ix + %2) - ld (hl),a -} by { - ld l,(ix + %1) - ld h,(ix + %2) - inc (hl) - ; peephole 93 incremented in (hl) instead of going through a. -} if notUsed('a') - -replace restart { - ld a,(hl) - inc a - ld (hl),a -} by { - inc (hl) - ; peephole 93' incremented in (hl) instead of going through a. -} if notUsed('a') - -// TODO: Check for volatile? -replace restart { - ld %1, %2 (%3) - inc %1 - ld %2 (%3), %1 -} by { - inc %2 (%3) - ld %1, %2 (%3) - ; peephole 93a incremented in %2 (%3) instead of going through %1. -} - -// TODO: Check for volatile? -replace restart { - ld %1, %2 (%3) - dec %1 - ld %2 (%3), %1 -} by { - dec %2 (%3) - ld %1, %2 (%3) - ; peephole 93b decremented in %2 (%3) instead of going through %1. -} - -replace restart { - ld %1,a - ld a,%2 - add a,%1 -} by { - ld %1, a - ; peephole 94 removed load by exploiting commutativity of addition. - add a,%2 -} if notSame(%2 '(bc)' '(de)'), canAssign('b' %2) -// canAssign('b' %2) is true, iff add a,%2 is possible. - -replace restart { - ld c,l - ld b,h - ld hl,#%1 - add hl,bc -} by { - ; peephole 95 removed loads by exploiting commutativity of addition. - ld bc,#%1 - add hl,bc -} if notUsed('bc') - -replace restart { - ld hl,#%1 - add hl,%2 - ld bc,#%4 - add hl,bc -} by { - ; peephole 96 removed loads by exploiting commutativity of addition. - ld hl,#%1 + %4 - add hl,%2 -} if notUsed('bc') - -replace restart { - ld c,e - ld b,d - ld hl,#%1 - add hl,bc -} by { - ; peephole 97 removed loads by exploiting commutativity of addition. - ld hl,#%1 - add hl,de -} if notUsed('bc') - -replace restart { - or a,%1 - jp NZ,%2 - ld %3,#0x00 -} by { - or a,%1 - jp NZ,%2 - ld %3,a - ; peephole 98 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - and a,%1 - jp NZ,%2 - ld %3,#0x00 -} by { - and a,%1 - jp NZ,%2 - ld %3,a - ; peephole 99 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - sub a,%1 - jp NZ,%2 - ld %3,#0x00 -} by { - sub a,%1 - jp NZ,%2 - ld %3,a - ; peephole 100 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - inc a - jp NZ,%1 - ld %2,#0x00 -} by { - inc a - jp NZ,%1 - ld %2,a - ; peephole 101 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - dec a - jp NZ,%1 - ld %2,#0x00 -} by { - dec a - jp NZ,%1 - ld %2,a - ; peephole 102 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - or a,%1 - jp NZ,%2 - ld a,%3 - or a,a -} by { - or a,%1 - jp NZ,%2 - or a,%3 - ; peephole 103 shortened or using a (which has just been tested to be #0x00). -} if canAssign('b' %3) -// canAssign('b' %2) is true, iff or a,%2 is possible. - -replace restart { - sub a,%1 - jp NZ,%2 - ld a,%3 - or a,a -} by { - sub a,%1 - jp NZ,%2 - or a,%3 - ; peephole 104 shortened or using a (which has just been tested to be #0x00). -} if canAssign('b' %3) -// canAssign('b' %2) is true, iff or a,%2 is possible. - -replace restart { - or a,%1 - jp NZ,%2 - push %3 - ld %4,#0x00 -} by { - or a,%1 - jp NZ,%2 - push %3 - ld %4,a - ; peephole 105 replaced constant #0x00 by a (which has just been tested to be #0x00). -} - -replace restart { - ld (hl),#0x00 - inc hl - ld (hl),#0x00 -} by { xor a, a - ; peephole 106 cached zero in a. - ld (hl), a - inc hl - ld (hl), a -} if notUsed('a') - -replace restart { - ld hl,#%1 - add hl,%2 - inc hl -} by { - ld hl,#%1+1 - add hl,%2 - ; peephole 107 moved increment of hl to constant. -} - -replace restart { - inc hl - ld %1,#%2 - add hl,%1 -} by { - ld %1,#%2+1 - add hl,%1 - ; peephole 108 moved increment of hl to constant. -} if notUsed(%1) - -replace restart { - dec hl - ld %1,#%2 - add hl,%1 -} by { - ld %1,#%2-1 - add hl,%1 - ; peephole 109 moved decrement of hl to constant. -} if notUsed(%1) - -replace restart { - inc iy - ld %1, (iy + %2) -} by { - ld %1, (iy + %2+1) - ; peephole 110 moved increment of iy to offset. -} if notUsed('iy') - -replace restart { - push hl - pop iy - pop hl - inc iy -} by { - inc hl - push hl - pop iy - pop hl - ; peephole 111 incremented in hl instead of iy. -} - -replace restart { - push hl - pop iy - inc iy -} by { - inc hl - push hl - pop iy - ; peephole 111a incremented in hl instead of iy. -} if notUsed('hl') - -replace restart { - push bc - pop iy - inc iy -} by { - inc bc - push bc - pop iy - ; peephole 111b incremented in bc instead of iy. -} if notUsed('bc') - -replace restart { - push de - pop iy - inc iy -} by { - inc de - push de - pop iy - ; peephole 111c incremented in de instead of iy. -} if notUsed('de') - -replace restart { - ld hl,%1 - add hl,%2 - push hl - pop iy -} by { - ld iy,%1 - add iy,%2 - ; peephole 111b added in iy instead of hl. -} if notUsed('hl'), notSame(%2 'hl') - -replace restart { - pop af - ld sp,%1 -} by { - ; peephole 112 removed redundant pop af. - ld sp,%1 -} if notUsed('a') - -replace restart { - inc sp - ld sp,%1 -} by { - ; peephole 113 removed redundant inc sp. - ld sp,%1 -} if notUsed('a') - -replace restart { - call %1 - ret -} by { - jp %1 - ; peephole 114 replaced call at end of function by jump (tail call optimization). -} - -// Callee saves ix. -replace restart { - call %1 - pop ix - ret -} by { - pop ix - jp %1 - ; peephole115 replaced call at end of function by jump moving call beyond pop ix (tail call optimization). -} - -replace restart { - ld %1,#%2 - ld %3,%4 - ld %1,#%2 -} by { - ld %1,#%2 - ld %3,%4 - ; peephole 116 removed load of #%2 into %1 since it's still there. -} if notVolatile(%1), operandsNotRelated(%3 %1) - -replace restart { - ld hl,#%1 - ld de,#%1 -} by { - ; peephole 117 used #%1 from hl for load into de. - ld hl,#%1 - ld e,l - ld d,h -} - -replace restart { - ld (ix + %1),l - ld (ix + %2),h - ld %3,(ix + %1) - ld %4,(ix + %2) -} by { - ld (ix + %1),l - ld (ix + %2),h - ; peephole 118 used hl instead of %2 (ix), %1 (ix) to load %4%3. - ld %3,l - ld %4,h -} if operandsNotRelated('h' %3) -// Don't check for volatile since ix points to the stack. - -replace restart { - ld %1, a - ld a, %2 (%3) - adc a, #%4 - ld %6, %1 -} by { - ld %6, a - ld a, %2 (%3) - adc a, #%4 - ; peephole 119 loaded %6 from a directly instead of going through %1. -} if notUsed(%1) - -replace restart { - ld %1, a - ld a, %2 (%3) - adc a, #%4 - ld %5, a - ld %6, %1 -} by { - ld %6, a - ld a, %2 (%3) - adc a, #%4 - ld %5, a - ; peephole 120 loaded %6 from a directly instead of going through %1. -} if notUsed(%1), notSame(%5 %1), notSame(%5 '(hl)' '(de)' '(bc)'), notSame(%5 %6), notSame(%6 '(hl)' '(de)' '(bc)'), notSame(%5 'a'), notSame(%6 'a') - -replace restart { - ld %1, a - ld a, #%2 - adc a, #%3 - ld %5, a - ld %6, %1 -} by { - ld %6, a - ld a, #%2 - adc a, #%3 - ld %5, a - ; peephole 121 loaded %6 from a directly instead of going through %1. -} if notUsed(%1), notSame(%5 %1), notSame(%5 %6 '(hl)' '(de)' '(bc)'), notSame(%6 'a') - -replace restart { - ld hl, #%1 - add hl, %2 - ex de, hl - ld hl, #%3 - add hl, de -} by { - ld hl, #%1+%3 - add hl, %2 - ; peephole 122 removed addition and loads exploiting commutativity of addition. -} if notUsed('de') - -replace restart { - ld %1,l - ld %2,h - ex de,hl - ld (hl),%1 - inc hl - ld (hl),%2 -} by { - ld %1,l - ex de,hl - ; peephole 122a used de instead of going through %1%2. - ld (hl),e - inc hl - ld (hl),d -} if notUsed(%2), notSame(%1 'l' 'h' 'e' 'd'), notSame(%2 'l' 'h' 'e' 'd') - -replace restart { - ld e, l - ld d, h - ld hl, #0x0001 - add hl, de -} by { - ld e, l - ld d, h - inc hl - ; peephole 123 replaced addition by increment. -} - -replace restart { - ld sp,hl - ld hl,#0x0002 - add hl,sp -} by { - ld sp, hl - inc hl - inc hl - ; peephole 124 replaced addition by increment. -} - -replace restart { - ex de, hl - ld hl, #%1 - add hl, de -} by { - ; peephole 125 removed ex exploiting commutativity of addition. - ld de, #%1 - add hl, de -} if notUsed('de') - -replace restart { - ex de, hl - push bc - ex de, hl -} by { - push bc - ; peephole 126 canceled subsequent ex de, hl. -} - -replace restart { - ld hl, #%1 - add hl, %2 - ex de, hl - inc de -} by { - ld hl, #%1+1 - ; peephole 127 moved increment to constant. - add hl, %2 - ex de, hl -} if notUsed('hl') - -replace restart { - ld a,#0x01 - jp %1 -%2: - xor a,a -%1: - sub a,#0x01 - ld a,#0x00 - rla -} by { - xor a,a - jp %1 -%2: - ld a,#0x01 -%1: - ; peephole 128 removed negation. -} if labelRefCount(%1 1) - -replace restart { - and a,#0x01 - sub a,#0x01 - ld a,#0x00 - rla -} by { - and a,#0x01 - xor a,#0x01 - ; peephole 129 used xor for negation. -} - -replace restart { - or a,a - sub a,#%1 -} by { - ; peephole 130 removed redundant or. - sub a,#%1 -} - -replace restart { - ld a,#0x00 - rla - sub a,#0x01 - ld a,#0x00 - rla -} by { - ld a,#0x00 - ccf - ; peephole 131 moved negation from bit 0 to carry flag. - rla -} - -replace restart { - ld a, #<(%1) - add a, l - ld l, a - ld a, #>(%1) - adc a, h - ld h, a - push bc -} by { - push bc - ld bc, #%1 - add hl, bc - ; peephole 132 used 16 bit addition by moving push bc - ld a, h -} if notUsed('bc') - -replace restart { - pop af - push hl -} by { - ; peephole 133 used ex to move hl onto the stack. - ex (sp),hl -} if notUsed('a'), notUsed('hl') - -replace restart { - pop af - ld hl, #%1 - push hl -} by { - ld hl, #%1 - ; peephole 134 used ex to move hl onto the stack. - ex (sp),hl -} if notUsed('a'), notUsed('hl') - -replace restart { - pop af - inc sp - ld hl,#%1 - push hl -} by { - inc sp - ld hl,#%1 - ; peephole 135 used ex to move #%1 onto the stack. - ex (sp),hl -} if notUsed('a'), notUsed('hl') - -replace restart { - pop af - ld a,#%1 - push af - inc sp -} by { - ld h,#%1 - ex (sp),hl - ; peephole 136 used ex to move #%1 onto the stack. - inc sp -} if notUsed('a'), notUsed('hl') - -replace restart { - ld %1,#%2 - ld %3 (%1),a -%4: - ld %1,%5 -} by { - ld (#%2 + %3),a - ; peephole 137 directly used #%2 instead of going through %1 using indirect addressing. -%4: - ld %1,%5 -} - -replace restart { - pop af - ld %1,#%2 - ld %3 (%1),%4 - ld %1,#%5 -} by { - ld a,%4 - ld (#%2 + %3),a - ; peephole 138 used #%2 directly instead of going through %1 using indirect addressing. - pop af - ld %1,#%5 -} if notSame(%3 'a') - -replace restart { - ld %1,a - bit %2,%1 -} by { - bit %2,a - ; peephole 139 tested bit %2 of a directly instead of going through %1. -} if notUsed(%1) - -replace restart { - sbc a,%1 - bit 7,a - jp Z,%2 -} by { - sbc a,%1 - jp P,%2 - ; peephole 140 used sign flag instead of testing bit 7. -} - -replace restart { - sbc a,%1 - bit 7,a - jp NZ,%2 -} by { - sbc a,%1 - jp M,%2 - ; peephole 141 used sign flag instead of testing bit 7. -} - -replace restart { - ld %1,a - or a,a - jp %3,%4 - ld a,%1 -} by { - ld %1,a - or a,a - jp %3,%4 - ; peephole 142 used value still in a instead of reloading from %1. -} - -replace { - jp %5 - ret -} by { - jp %5 - ; peephole 143 removed unused ret. -} - -replace { - jp %5 - ld sp,ix - pop ix - ret -} by { - jp %5 - ; peephole 144 removed unused ret. -} - -replace restart { - or a,%1 - jp NZ,%2 - xor a,a - jp %3 -} by { - or a,%1 - jp NZ,%2 - ; peephole 145 removed redundant zeroing of a (which has just been tested to be #0x00). - jp %3 -} - -// These ex-generating rules should be among the last ones since ex counts as a read from both hl and de for notUsed(). -barrier - -replace restart { - ld d,h - ld e,l -} by { - ; peephole 146 used ex to load hl into de. - ex de,hl -} if notUsed('hl') - -replace restart { - ld e,l - ld d,h -} by { - ; peephole 147 used ex to load hl into de. - ex de,hl -} if notUsed('hl') - -replace restart { - ld l,e - ld h,d -} by { - ; peephole 148 used ex to load de into hl. - ex de,hl -} if notUsed('de') - -barrier - -// Should be one of the last ones. Opens the code to further peephole optimization. -replace restart { -%1: -} by { - ; peephole 149 removed unused label %1. -} if labelRefCount(%1 0) - -// Ensure that all rules above see only jp, not jr. -barrier - -// Do all jump optimizations before replacing by ret. - -replace restart { - jp %5 -} by { - ret - ; peephole 151 replaced jump by return. -} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) - -replace restart { - jp %1,%5 + ld %1 (%2), a } by { - ret %1 - ; peephole 152 replaced jump by return. -} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + ld %1 (%2), #0 + ; peephole 178 direct load zero constant to memory addressed by index register +} if notUsed('a'), canAssign(%1 %2 '#0') diff --git a/src/backend/ralloc.c b/src/backend/ralloc.c index d5d7db2dd..59c5ff1ff 100644 --- a/src/backend/ralloc.c +++ b/src/backend/ralloc.c @@ -43,9 +43,9 @@ software-hoarding! */ +#include "z80.h" #include "SDCCicode.h" #include "dbuf_string.h" -#include "z80.h" /* Flags to turn off optimisations. */ @@ -72,6 +72,8 @@ enum { D_PACK_HLUSE3 = 0 }; +// #define D_ALLOC 1 + #if 1 #define D(_a, _s) \ if (_a) { \ @@ -85,11 +87,9 @@ enum { #define DISABLE_PACKREGSFORSUPPORT 1 #define DISABLE_PACKREGSFORACCUSE 1 -// Build the old allocator. It can be used by command-line options +// Build the old register allocator. It can be used by command-line options #define OLDRALLOC 1 -extern void genZ80Code(iCode *); - /** Local static variables */ static struct { bitVect *spiltSet; @@ -106,16 +106,17 @@ static struct { } _G; static reg_info _gbz80_regs[] = { - {REG_GPR, C_IDX, "c", 1}, {REG_GPR, B_IDX, "b", 1}, - {REG_GPR, E_IDX, "e", 1}, {REG_GPR, D_IDX, "d", 1}, - {REG_GPR, L_IDX, "l", 1}, {REG_GPR, H_IDX, "h", 1}, - {REG_CND, CND_IDX, "c", 1}}; + {REG_GPR, A_IDX, "a", 1}, {REG_GPR, C_IDX, "c", 1}, + {REG_GPR, B_IDX, "b", 1}, {REG_GPR, E_IDX, "e", 1}, + {REG_GPR, D_IDX, "d", 1}, {REG_GPR, L_IDX, "l", 1}, + {REG_GPR, H_IDX, "h", 1}, {REG_CND, CND_IDX, "c", 1}}; static reg_info _z80_regs[] = { - {REG_GPR, C_IDX, "c", 1}, {REG_GPR, B_IDX, "b", 1}, - {REG_GPR, E_IDX, "e", 1}, {REG_GPR, D_IDX, "d", 1}, - {REG_GPR, L_IDX, "l", 1}, {REG_GPR, H_IDX, "h", 1}, - {REG_CND, CND_IDX, "c", 1}}; + {REG_GPR, A_IDX, "a", 1}, {REG_GPR, C_IDX, "c", 1}, + {REG_GPR, B_IDX, "b", 1}, {REG_GPR, E_IDX, "e", 1}, + {REG_GPR, D_IDX, "d", 1}, {REG_GPR, L_IDX, "l", 1}, + {REG_GPR, H_IDX, "h", 1}, {REG_GPR, IYL_IDX, "iyl", 1}, + {REG_GPR, IYH_IDX, "iyh", 1}, {REG_CND, CND_IDX, "c", 1}}; reg_info *regsZ80; @@ -123,7 +124,7 @@ reg_info *regsZ80; #define Z80_MAX_REGS ((sizeof(_z80_regs) / sizeof(_z80_regs[0])) - 1) #define GBZ80_MAX_REGS ((sizeof(_gbz80_regs) / sizeof(_gbz80_regs[0])) - 1) -void spillThis(symbol *); +void z80SpillThis(symbol *); static void freeAllRegs(); #ifdef OLDRALLOC @@ -136,7 +137,7 @@ static void freeAllRegs(); static reg_info *allocReg(short type) { int i; - for (i = 0; i < _G.nRegs; i++) { + for (i = C_IDX; i < _G.nRegs; i++) { /* For now we allocate from any free */ if (regsZ80[i].isFree) { regsZ80[i].isFree = 0; @@ -157,7 +158,7 @@ static reg_info *allocReg(short type) { reg_info *regWithIdx(int idx) { int i; - for (i = 0; i < _G.nRegs; i++) { + for (i = C_IDX; i < _G.nRegs; i++) { if (regsZ80[i].rIdx == idx) { return ®sZ80[i]; } @@ -182,7 +183,7 @@ static int nFreeRegs(int type) { int i; int nfr = 0; - for (i = 0; i < _G.nRegs; i++) { + for (i = C_IDX; i < _G.nRegs; i++) { /* For now only one reg type */ if (regsZ80[i].isFree) { nfr++; @@ -258,7 +259,7 @@ static int hasSpilLoc(symbol *sym, eBBlock *ebp, iCode *ic) { #ifdef OLDRALLOC /** Will return 1 if the remat flag is set. - A symbol is rematerialisable if it doesnt need to be allocated + A symbol is rematerialisable if it doesn't need to be allocated into registers at creation as it can be re-created at any time - i.e. it's constant in some way. */ @@ -390,11 +391,11 @@ static symbol *createStackSpil(symbol *sym) { symbol *sloc = NULL; struct dbuf_s dbuf; - D(D_ALLOC, ("createStackSpil: for sym %p\n", sym)); + D(D_ALLOC, ("createStackSpil: for sym %p (%s)\n", sym, sym->name)); /* first go try and find a free one that is already existing on the stack */ - if (applyToSet(_G.stackSpil, isFree, &sloc, sym)) { + if (applyToSet(_G.stackSpil, isFree, &sloc, sym) && USE_OLDSALLOC) { /* found a free one : just update & return */ sym->usl.spillLoc = sloc; sym->stackSpil = 1; @@ -445,28 +446,33 @@ static symbol *createStackSpil(symbol *sym) { of the spill location */ addSetHead(&sloc->usl.itmpStack, sym); - D(D_ALLOC, ("createStackSpil: created new\n")); + D(D_ALLOC, + ("createStackSpil: created new %s for %s\n", sloc->name, sym->name)); return sym; } /*-----------------------------------------------------------------*/ /* spillThis - spils a specific operand */ /*-----------------------------------------------------------------*/ -void spillThis(symbol *sym) { +void z80SpillThis(symbol *sym) { int i; - D(D_ALLOC, ("spillThis: spilling %p\n", sym)); - - sym->for_newralloc = 0; + D(D_ALLOC, ("z80SpillThis: spilling %p (%s)\n", sym, sym->name)); /* if this is rematerializable or has a spillLocation we are okay, else we need to create a spillLocation for it */ - if (!(sym->remat || sym->usl.spillLoc)) { + if (!(sym->remat || sym->usl.spillLoc) || + (sym->usl.spillLoc && + !sym->usl.spillLoc + ->onStack)) // z80 port currently only supports on-stack spill + // locations in code generation. createStackSpil(sym); - } + else + D(D_ALLOC, ("Already has spilllocation %p, %s\n", sym->usl.spillLoc, + sym->usl.spillLoc->name)); - /* mark it has spilt & put it in the spilt set */ + /* mark it as spilt & put it in the spilt set */ sym->isspilt = sym->spillA = 1; _G.spiltSet = bitVectSetBit(_G.spiltSet, sym->key); @@ -585,8 +591,8 @@ symbol *selectSpil(iCode *ic, eBBlock *ebp, symbol *forSym) { /* this is an extreme situation we will spill this one : happens very rarely but it does happen */ - D(D_ALLOC, ("selectSpil: using spillThis.\n")); - spillThis(forSym); + D(D_ALLOC, ("selectSpil: using z80SpillThis.\n")); + z80SpillThis(forSym); return forSym; } #endif @@ -904,7 +910,7 @@ static int positionRegs(symbol *result, symbol *opsym) { bool tryAllocatingRegPair(symbol *sym) { int i; wassert(sym->nRegs == 2); - for (i = 0; i < _G.nRegs; i += 2) { + for (i = C_IDX; i < _G.nRegs; i += 2) { if ((regsZ80[i].isFree) && (regsZ80[i + 1].isFree)) { regsZ80[i].isFree = 0; sym->regs[0] = ®sZ80[i]; @@ -946,12 +952,11 @@ static void verifyRegsAssigned(operand *op, iCode *ic) { if (sym->regs[0]) return; - // Don't warn for new allocator, since this is not used by default (until - // Thoruop is implemented for spillocation compaction). + // Don't warn for new allocator, since this is now used by default. if (options.oldralloc) werrorfl(ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name); - spillThis(sym); + z80SpillThis(sym); } #ifdef OLDRALLOC @@ -1002,7 +1007,8 @@ static void serialRegAssign(eBBlock **ebbs, int count) { int willCS; int j; - D(D_ALLOC, ("serialRegAssign: in loop on result %p\n", sym)); + D(D_ALLOC, + ("serialRegAssign: in loop on result %p %s\n", sym, sym->name)); /* Make sure any spill location is definately allocated */ if (sym->isspilt && !sym->remat && sym->usl.spillLoc && @@ -1025,7 +1031,7 @@ static void serialRegAssign(eBBlock **ebbs, int count) { to be safe */ if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) { D(D_ALLOC, ("serialRegAssign: \"spilling to be safe.\"\n")); - spillThis(sym); + z80SpillThis(sym); continue; } /* if trying to allocate this will cause @@ -1037,7 +1043,7 @@ static void serialRegAssign(eBBlock **ebbs, int count) { if (sym->remat || (willCS && bitVectIsZero(spillable))) { D(D_ALLOC, ("serialRegAssign: \"remat spill\"\n")); - spillThis(sym); + z80SpillThis(sym); continue; } @@ -1047,7 +1053,7 @@ static void serialRegAssign(eBBlock **ebbs, int count) { before ic->seq. This is complicated, so spill this symbol instead and let fillGaps handle the allocation. */ if (sym->liveFrom < ic->seq) { - spillThis(sym); + z80SpillThis(sym); continue; } @@ -1058,7 +1064,7 @@ static void serialRegAssign(eBBlock **ebbs, int count) { symbol *leastUsed = leastUsedLR(liveRangesWith(spillable, allLRs, ebbs[i], ic)); if (leastUsed && leastUsed->used > sym->used) { - spillThis(sym); + z80SpillThis(sym); continue; } } else { @@ -1070,7 +1076,7 @@ static void serialRegAssign(eBBlock **ebbs, int count) { */ if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) { - spillThis(sym); + z80SpillThis(sym); continue; } } @@ -1296,10 +1302,10 @@ bitVect *rUmaskForOp(const operand *op) { if (sym->isspilt || !sym->nRegs) return NULL; - rumask = newBitVect(_G.nRegs + (IS_GB ? 0 : 2)); + rumask = newBitVect(_G.nRegs + (2)); for (j = 0; j < sym->nRegs; j++) { - if (!(sym->regs[j]) || sym->regs[j]->rIdx < C_IDX || + if (!(sym->regs[j]) || sym->regs[j]->rIdx < 0 || sym->regs[j]->rIdx > CND_IDX) { werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "rUmaskForOp: Register not found"); @@ -1316,7 +1322,7 @@ bitVect *z80_rUmaskForOp(const operand *op) { return rUmaskForOp(op); } /** Returns bit vector of registers used in iCode. */ bitVect *regsUsedIniCode(iCode *ic) { - bitVect *rmask = newBitVect(_G.nRegs + (IS_GB ? 0 : 2)); + bitVect *rmask = newBitVect(_G.nRegs + (2)); /* do the special cases first */ if (ic->op == IFX) { @@ -1376,7 +1382,7 @@ static void createRegMask(eBBlock **ebbs, int count) { /* now create the register mask for those registers that are in use : this is a super set of ic->rUsed */ - ic->rMask = newBitVect(_G.nRegs + 1 + (IS_GB ? 0 : 2)); + ic->rMask = newBitVect(_G.nRegs + 1 + (2)); /* for all live Ranges alive at this point */ for (j = 1; j < ic->rlive->size; j++) { @@ -1472,8 +1478,14 @@ static void regTypeNum(void) { for (sym = hTabFirstItem(liveRanges, &k); sym; sym = hTabNextItem(liveRanges, &k)) { /* if used zero times then no registers needed */ - if ((sym->liveTo - sym->liveFrom) == 0) + if ((sym->liveTo - sym->liveFrom) == 0 && getSize(sym->type) <= 4) continue; + else if ((sym->liveTo - sym->liveFrom) == 0 && + bitVectnBitsOn(sym->defs) <= 1) { + iCode *dic = hTabItemWithKey(iCodehTab, bitVectFirstBit(sym->defs)); + if (!dic || dic->op != CALL && dic->op != PCALL) + continue; + } D(D_ALLOC, ("regTypeNum: loop on sym %p\n", sym)); @@ -1504,7 +1516,7 @@ static void regTypeNum(void) { D(D_ALLOC, ("regTypeNum: setup to assign regs sym %p\n", sym)); if (sym->nRegs > 8) { - fprintf(stderr, "allocated more than 8 egisters for type "); + fprintf(stderr, "allocated more than 8 registers for type "); printTypeChain(sym->type, stderr); fprintf(stderr, "\n"); } @@ -1529,14 +1541,14 @@ static void freeAllRegs() { D(D_ALLOC, ("freeAllRegs: running.\n")); - for (i = 0; i < _G.nRegs; i++) + for (i = C_IDX; i < _G.nRegs; i++) regsZ80[i].isFree = 1; } /*-----------------------------------------------------------------*/ /* deallocStackSpil - this will set the stack pointer back */ /*-----------------------------------------------------------------*/ -DEFSETFUNC(deallocStackSpil) { +static DEFSETFUNC(deallocStackSpil) { symbol *sym = item; deallocLocal(sym); @@ -1564,7 +1576,8 @@ static int packRegsForAssign(iCode *ic, eBBlock *ebp) { we cannot */ for (dic = ic->prev; dic; dic = dic->prev) { /* PENDING: Don't pack across function calls. */ - if (dic->op == CALL || dic->op == PCALL) { + if (dic->op == CALL || dic->op == PCALL || dic->op == INLINEASM || + dic->op == CRITICAL || dic->op == ENDCRITICAL) { dic = NULL; break; } @@ -1641,17 +1654,18 @@ static int packRegsForAssign(iCode *ic, eBBlock *ebp) { (IC_RIGHT(dic) && IC_RESULT(ic)->key == IC_RIGHT(dic)->key))) return 0; } + pack: + /* Keep assignment if it is an sfr write - not all of code generation can + * deal with result in sfr */ + if (IC_RESULT(ic) && IS_TRUE_SYMOP(IC_RESULT(ic)) && + SPEC_OCLS(OP_SYMBOL(IC_RESULT(ic))->etype) && + IN_REGSP(SPEC_OCLS(OP_SYMBOL(IC_RESULT(ic))->etype)) && + (dic->op == LEFT_OP || dic->op == RIGHT_OP)) + return 0; + /* found the definition */ - /* replace the result with the result of */ - /* this assignment and remove this assignment */ - bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs, dic->key); - IC_RESULT(dic) = IC_RESULT(ic); - if (IS_ITEMP(IC_RESULT(dic)) && - OP_SYMBOL(IC_RESULT(dic))->liveFrom > dic->seq) { - OP_SYMBOL(IC_RESULT(dic))->liveFrom = dic->seq; - } /* delete from liverange table also delete from all the points inbetween and the new one */ @@ -1661,6 +1675,16 @@ static int packRegsForAssign(iCode *ic, eBBlock *ebp) { bitVectSetBit(sic->rlive, IC_RESULT(dic)->key); } + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs, dic->key); + IC_RESULT(dic) = IC_RESULT(ic); + + if (IS_ITEMP(IC_RESULT(dic)) && + OP_SYMBOL(IC_RESULT(dic))->liveFrom > dic->seq) { + OP_SYMBOL(IC_RESULT(dic))->liveFrom = dic->seq; + } + remiCodeFromeBBlock(ebp, ic); // PENDING: Check vs mcs51 bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs, ic->key); @@ -1831,7 +1855,7 @@ static iCode *packRegsForOneuse(iCode *ic, operand *op, eBBlock *ebp) { if (bitVectnBitsOn(OP_DEFS(op)) > 1) return NULL; /* has more than one definition */ - /* get the that definition */ + /* get that definition */ if (!(dic = hTabItemWithKey(iCodehTab, bitVectFirstBit(OP_DEFS(op))))) return NULL; @@ -2443,15 +2467,16 @@ static void packRegisters(eBBlock *ebp) { } for (ic = ebp->sch; ic; ic = ic->next) { + D(D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); + /* Safe: address of a true sym is always constant. */ /* if this is an itemp & result of a address of a true sym then mark this as rematerialisable */ - D(D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); - if (ic->op == ADDRESS_OF && IS_ITEMP(IC_RESULT(ic)) && - IS_TRUE_SYMOP(IC_LEFT(ic)) && bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) == 1 && - !OP_SYMBOL(IC_LEFT(ic))->onStack) { + !IS_PARM(IC_RESULT( + ic)) /* The receiving of the paramter isnot accounted for in DEFS */ + && IS_TRUE_SYMOP(IC_LEFT(ic)) && !OP_SYMBOL(IC_LEFT(ic))->onStack) { OP_SYMBOL(IC_RESULT(ic))->remat = 1; OP_SYMBOL(IC_RESULT(ic))->rematiCode = ic; OP_SYMBOL(IC_RESULT(ic))->usl.spillLoc = NULL; @@ -2461,10 +2486,11 @@ static void packRegisters(eBBlock *ebp) { /* if straight assignment then carry remat flag if this is the only definition */ if (ic->op == '=' && !POINTER_SET(ic) && IS_SYMOP(IC_RIGHT(ic)) && - OP_SYMBOL(IC_RIGHT(ic))->remat && - !IS_CAST_ICODE(OP_SYMBOL(IC_RIGHT(ic))->rematiCode) && - !isOperandGlobal(IC_RESULT(ic)) && /* due to bug 1618050 */ - bitVectnBitsOn(OP_SYMBOL(IC_RESULT(ic))->defs) <= 1) { + OP_SYMBOL(IC_RIGHT(ic))->remat && !isOperandGlobal(IC_RESULT(ic)) && + bitVectnBitsOn(OP_SYMBOL(IC_RESULT(ic))->defs) == 1 && + !IS_PARM(IC_RESULT(ic)) && /* The receiving of the paramter isnot + accounted for in DEFS */ + !OP_SYMBOL(IC_RESULT(ic))->addrtaken) { OP_SYMBOL(IC_RESULT(ic))->remat = OP_SYMBOL(IC_RIGHT(ic))->remat; OP_SYMBOL(IC_RESULT(ic))->rematiCode = OP_SYMBOL(IC_RIGHT(ic))->rematiCode; @@ -2473,11 +2499,14 @@ static void packRegisters(eBBlock *ebp) { /* if cast to a generic pointer & the pointer being cast is remat, then we can remat this cast as well */ if (ic->op == CAST && IS_SYMOP(IC_RIGHT(ic)) && - OP_SYMBOL(IC_RIGHT(ic))->remat && - bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) == 1) { + OP_SYMBOL(IC_RIGHT(ic))->remat && !isOperandGlobal(IC_RESULT(ic)) && + bitVectnBitsOn(OP_DEFS(IC_RESULT(ic))) == 1 && + !IS_PARM(IC_RESULT(ic)) && /* The receiving of the paramter isnot + accounted for in DEFS */ + !OP_SYMBOL(IC_RESULT(ic))->addrtaken) { sym_link *to_type = operandType(IC_LEFT(ic)); sym_link *from_type = operandType(IC_RIGHT(ic)); - if (IS_GENPTR(to_type) && IS_PTR(from_type)) { + if ((IS_PTR(to_type) || IS_INT(to_type)) && IS_PTR(from_type)) { OP_SYMBOL(IC_RESULT(ic))->remat = 1; OP_SYMBOL(IC_RESULT(ic))->rematiCode = ic; OP_SYMBOL(IC_RESULT(ic))->usl.spillLoc = NULL; @@ -2512,12 +2541,6 @@ static void packRegisters(eBBlock *ebp) { continue; } -#if 0 - /* reduce for support function calls */ - if (ic->supportRtn || ic->op == '+' || ic->op == '-') - packRegsForSupport (ic, ebp); -#endif - /* some cases the redundant moves can can be eliminated for return statements */ if (ic->op == RETURN || ic->op == SEND) { @@ -2552,11 +2575,11 @@ static void packRegisters(eBBlock *ebp) { if ((options.oldralloc || !OPTRALLOC_HL) && !DISABLE_PACK_HL && IS_ITEMP(IC_RESULT(ic))) - if (!IS_GB && !IY_RESERVED) + if (!IY_RESERVED) packRegsForHLUse3(ic, IC_RESULT(ic), ebp); if ((options.oldralloc || !OPTRALLOC_IY) && !DISABLE_PACK_IY && - !IY_RESERVED && IS_ITEMP(IC_RESULT(ic)) && !IS_GB) + !IY_RESERVED && IS_ITEMP(IC_RESULT(ic))) packRegsForIYUse(ic, IC_RESULT(ic), ebp); if (options.oldralloc && !DISABLE_PACK_ACC && IS_ITEMP(IC_RESULT(ic)) && @@ -2568,7 +2591,7 @@ static void packRegisters(eBBlock *ebp) { /** Joins together two byte constant pushes into one word push. */ static iCode *joinPushes(iCode *lic) { - iCode *ic, *uic; + iCode *ic, *uic, *fic; for (ic = lic; ic; ic = ic->next) { int first, second; @@ -2578,17 +2601,30 @@ static iCode *joinPushes(iCode *lic) { uic = ic->next; /* Anything past this? */ - if (uic == NULL) { + if (uic == NULL) continue; - } + /* This and the next pushes? */ - if (ic->op != IPUSH || uic->op != IPUSH) { + if (ic->op != IPUSH || uic->op != IPUSH) continue; + + /* Find call */ + for (fic = uic; fic->op == IPUSH; fic = fic->next) + ; + if (ic->op != CALL && fic->op != PCALL) + continue; + { + sym_link *dtype = operandType(IC_LEFT(fic)); + sym_link *ftype = IS_FUNCPTR(dtype) ? dtype->next : dtype; + if (IFFUNC_ISSMALLC(ftype)) /* SmallC calling convention pushes 8-bit + values as 16 bit */ + continue; } + /* Both literals? */ - if (!IS_OP_LITERAL(IC_LEFT(ic)) || !IS_OP_LITERAL(IC_LEFT(uic))) { + if (!IS_OP_LITERAL(IC_LEFT(ic)) || !IS_OP_LITERAL(IC_LEFT(uic))) continue; - } + /* Both characters? */ if (getSize(operandType(IC_LEFT(ic))) != 1 || getSize(operandType(IC_LEFT(uic))) != 1) { @@ -2604,8 +2640,7 @@ static iCode *joinPushes(iCode *lic) { val = constVal(dbuf_c_str(&dbuf)); dbuf_destroy(&dbuf); SPEC_NOUN(val->type) = V_INT; - IC_LEFT(ic) = operandFromOperand(IC_LEFT(ic)); - OP_VALUE(IC_LEFT(ic)) = val; + IC_LEFT(ic) = operandFromValue(val); /* Now remove the second one from the list. */ ic->next = uic->next; @@ -2668,7 +2703,8 @@ static void serialRegMark(eBBlock **ebbs, int count) { if (IC_RESULT(ic)) { symbol *sym = OP_SYMBOL(IC_RESULT(ic)); - D(D_ALLOC, ("serialRegAssign: in loop on result %p\n", sym)); + D(D_ALLOC, + ("serialRegAssign: in loop on result %p (%s)\n", sym, sym->name)); /* Make sure any spill location is definately allocated */ if (sym->isspilt && !sym->remat && sym->usl.spillLoc && @@ -2681,7 +2717,8 @@ static void serialRegMark(eBBlock **ebbs, int count) { or will not live beyond this instructions */ if (!sym->nRegs || sym->isspilt || bitVectBitValue(_G.regAssigned, sym->key) || sym->for_newralloc || - sym->liveTo <= ic->seq) { + (sym->liveTo <= ic->seq && + (sym->nRegs <= 4 || ic->op != CALL && ic->op != PCALL))) { D(D_ALLOC, ("serialRegAssign: won't live long enough.\n")); continue; } @@ -2691,15 +2728,36 @@ static void serialRegMark(eBBlock **ebbs, int count) { to be safe */ if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) { D(D_ALLOC, ("serialRegAssign: \"spilling to be safe.\"\n")); - spillThis(sym); + sym->for_newralloc = 0; + z80SpillThis(sym); continue; } - if (max_alloc_bytes >= sym->nRegs) { + if (sym->usl.spillLoc && !sym->usl.spillLoc->_isparm && + !USE_OLDSALLOC) // I have no idea where these spill locations come + // from. Sometime two symbols even have the same + // spill location, whic tends to mess up stack + // allocation. Those that come from previous + // iterations in this loop would be okay, but those + // from outside are a problem. + { + sym->usl.spillLoc = 0; + sym->isspilt = false; + } + + if (sym->nRegs > + 4) /* TODO. Change this once we can allocate bigger variables (but + still spill when its a big return value). Also change in + ralloc2.cc, operand_on-stack in that case*/ + { + D(D_ALLOC, ("Spilling %s (too large)\n", sym->name)); + sym->for_newralloc = 0; + z80SpillThis(sym); + } else if (max_alloc_bytes >= sym->nRegs) { sym->for_newralloc = 1; max_alloc_bytes -= sym->nRegs; } else if (!sym->for_newralloc) { - spillThis(sym); + z80SpillThis(sym); printf("Spilt %s due to byte limit.\n", sym->name); } } @@ -2707,7 +2765,7 @@ static void serialRegMark(eBBlock **ebbs, int count) { } } -static void RegFix(eBBlock **ebbs, int count) { +void Z80RegFix(eBBlock **ebbs, int count) { int i; /* Check for and fix any problems with uninitialized operands */ @@ -2753,20 +2811,15 @@ void z80_oldralloc(ebbIndex *ebbi) { iCode *ic; int i; - D(D_ALLOC, ("\n-> z80_assignRegisters: entered.\n")); + D(D_ALLOC, ("\n-> z80_oldralloc: entered for %s.\n", + currFunc ? currFunc->name : "[no function]")); setToNull((void *)&_G.funcrUsed); setToNull((void *)&_G.totRegAssigned); _G.stackExtend = _G.dataExtend = 0; - if (IS_GB) { - /* DE is required for the code gen. */ - _G.nRegs = 2; - regsZ80 = _gbz80_regs; - } else { - _G.nRegs = 4; - regsZ80 = _z80_regs; - } + _G.nRegs = 5; + regsZ80 = _z80_regs; z80_init_asmops(); @@ -2845,20 +2898,15 @@ void z80_ralloc(ebbIndex *ebbi) { iCode *ic; int i; - D(D_ALLOC, ("\n-> z80_assignRegisters: entered.\n")); + D(D_ALLOC, ("\n-> z80_ralloc: entered for %s.\n", + currFunc ? currFunc->name : "[no function]")); setToNull((void *)&_G.funcrUsed); setToNull((void *)&_G.totRegAssigned); _G.stackExtend = _G.dataExtend = 0; - if (IS_GB) { - /* DE is required for the code gen. */ - _G.nRegs = GBZ80_MAX_REGS; - regsZ80 = _gbz80_regs; - } else { _G.nRegs = Z80_MAX_REGS; regsZ80 = _z80_regs; - } z80_init_asmops(); @@ -2881,34 +2929,16 @@ void z80_ralloc(ebbIndex *ebbi) { /* Mark variables for assignment by the new allocator */ serialRegMark(ebbs, count); + joinPushes(iCodeLabelOptimize(iCodeFromeBBlock(ebbs, count))); + /* The new register allocator invokes its magic */ ic = z80_ralloc2_cc(ebbi); - RegFix(ebbs, count); - - /* if stack was extended then tell the user */ - if (_G.stackExtend) { - /* werror(W_TOOMANY_SPILS,"stack", */ - /* _G.stackExtend,currFunc->name,""); */ - _G.stackExtend = 0; - } - - if (_G.dataExtend) { - /* werror(W_TOOMANY_SPILS,"data space", */ - /* _G.dataExtend,currFunc->name,""); */ - _G.dataExtend = 0; - } - if (options.dump_i_code) { dumpEbbsToFileExt(DUMP_RASSGN, ebbi); dumpLiveRanges(DUMP_LRANGE, liveRanges); } - ic = joinPushes(ic); - - /* redo that offsets for stacked automatic variables */ - redoStackOffsets(); - genZ80Code(ic); /* free up any stackSpil locations allocated */ diff --git a/src/backend/ralloc.h b/src/backend/ralloc.h index e48f7ed3a..a91aa7c24 100644 --- a/src/backend/ralloc.h +++ b/src/backend/ralloc.h @@ -29,8 +29,11 @@ #define DEBUG_FAKE_EXTRA_REGS 0 +#define USE_OLDSALLOC 0 // Change to 1 to use old stack allocator + enum { - C_IDX = 0, + A_IDX = 0, + C_IDX, B_IDX, E_IDX, D_IDX, @@ -48,10 +51,14 @@ enum { S_IDX, T_IDX, #endif - CND_IDX -}; + CND_IDX, -#define A_IDX (IS_GB ? 4 : (IY_RESERVED ? 6 : 8)) + // These pairs are for internal use in code generation only. + IY_IDX, + BC_IDX, + DE_IDX, + HL_IDX +}; enum { REG_PTR = 1, REG_GPR = 2, REG_CND = 4, REG_PAIR = 8 }; @@ -72,7 +79,8 @@ reg_info *regWithIdx(int); void z80_assignRegisters(ebbIndex *); bitVect *z80_rUmaskForOp(const operand *op); -void spillThis(symbol *); +void z80SpillThis(symbol *); iCode *z80_ralloc2_cc(ebbIndex *ebbi); +void Z80RegFix(eBBlock **ebbs, int count); #endif diff --git a/src/backend/ralloc2.cc b/src/backend/ralloc2.cc index 1dc4670ad..198c78864 100644 --- a/src/backend/ralloc2.cc +++ b/src/backend/ralloc2.cc @@ -19,29 +19,30 @@ // // An optimal, polynomial-time register allocator. -//#define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing +// #define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing // register allocation on the tree decomposition. #define DEBUG_RALLOC_DEC_ASS // // Uncomment to get debug messages about assignments while doing register // allocation on the tree decomposition (much more verbose than the one above). #include "SDCCralloc.hpp" +#include "../SDCCsalloc.hpp" extern "C" { #include "z80.h" unsigned char dryZ80iCode(iCode *ic); bool z80_assignment_optimal; bool should_omit_frame_ptr; -}; - -#define REG_C 0 -#define REG_B 1 -#define REG_E 2 -#define REG_D 3 -#define REG_L 4 -#define REG_H 5 -#define REG_IYL 6 -#define REG_IYH 7 -#define REG_A (port->num_regs - 1) +} + +#define REG_A 0 +#define REG_C 1 +#define REG_B 2 +#define REG_E 3 +#define REG_D 4 +#define REG_L 5 +#define REG_H 6 +#define REG_IYL 7 +#define REG_IYH 8 template float default_operand_cost(const operand *o, const assignment &a, @@ -60,14 +61,14 @@ float default_operand_cost(const operand *o, const assignment &a, var_t v = oi->second; // In registers. - if (a.local.find(v) != a.local.end()) { + if (std::binary_search(a.local.begin(), a.local.end(), v)) { c += 1.0f; byteregs[I[v].byte] = a.global[v]; size = 1; while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) != a.local.end() + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); byteregs[I[v].byte] = a.global[v]; @@ -78,13 +79,13 @@ float default_operand_cost(const operand *o, const assignment &a, // Todo: Extend this once the register allcoator can use registers other // than bc, de: if ((size == 2 || size == 4) && - (byteregs[1] != byteregs[0] + 1 || byteregs[0] != REG_C && - byteregs[0] != REG_E && - byteregs[0] != REG_L)) + (byteregs[1] != byteregs[0] + 1 || + (byteregs[0] != REG_C && byteregs[0] != REG_E && + byteregs[0] != REG_L))) c += 2.0f; if (size == 4 && (byteregs[3] != byteregs[2] + 1 || - byteregs[2] != REG_C && byteregs[2] != REG_E && - byteregs[0] != REG_L)) + (byteregs[2] != REG_C && byteregs[2] != REG_E && + byteregs[0] != REG_L))) c += 2.0f; // Code generator cannot handle variables only partially in A. @@ -97,7 +98,7 @@ float default_operand_cost(const operand *o, const assignment &a, c -= 0.4f; else if (OPTRALLOC_HL && byteregs[0] == REG_L) c -= 0.1f; - else if (OPTRALLOC_IY && byteregs[0] == REG_IYL || + else if ((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) c += 0.1f; } @@ -106,7 +107,7 @@ float default_operand_cost(const operand *o, const assignment &a, c += OP_SYMBOL_CONST(o)->remat ? 1.5f : 4.0f; while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) == a.local.end() + c += (!std::binary_search(a.local.begin(), a.local.end(), v) ? 4.0f : std::numeric_limits::infinity()); } @@ -131,13 +132,13 @@ static bool operand_sane(const operand *o, const assignment &a, return (true); // In registers. - if (a.local.find(oi->second) != a.local.end()) { + if (std::binary_search(a.local.begin(), a.local.end(), oi->second)) { while (++oi != oi_end) - if (a.local.find(oi->second) == a.local.end()) + if (!std::binary_search(a.local.begin(), a.local.end(), oi->second)) return (false); } else { while (++oi != oi_end) - if (a.local.find(oi->second) != a.local.end()) + if (std::binary_search(a.local.begin(), a.local.end(), oi->second)) return (false); } @@ -188,8 +189,9 @@ static float assign_cost(const assignment &a, unsigned short int i, POINTER_GET(ic) || POINTER_SET(ic)) return (default_instruction_cost(a, i, G, I)); - reg_t byteregs[4] = {-1, -1, -1, -1}; // Todo: Change this when sdcc supports - // variables larger than 4 bytes. + reg_t byteregs[4] = { + -1, -1, -1, -1}; // Todo: Change this when sdcc supports variables larger + // than 4 bytes in register allocation for z80. operand_map_t::const_iterator oi, oi_end; @@ -200,7 +202,7 @@ static float assign_cost(const assignment &a, unsigned short int i, if (oi != oi_end) { var_t v = oi->second; - if (a.local.find(v) == a.local.end()) + if (!std::binary_search(a.local.begin(), a.local.end(), v)) return (default_instruction_cost(a, i, G, I)); c += 1.0f; @@ -209,7 +211,7 @@ static float assign_cost(const assignment &a, unsigned short int i, while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) != a.local.end() + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); byteregs[I[v].byte] = a.global[v]; @@ -224,7 +226,7 @@ static float assign_cost(const assignment &a, unsigned short int i, if (byteregs[0] == REG_A) c -= 0.4f; - else if (OPTRALLOC_IY && byteregs[0] == REG_IYL || byteregs[0] == REG_IYH) + else if ((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) c += 0.1f; } @@ -236,7 +238,7 @@ static float assign_cost(const assignment &a, unsigned short int i, if (oi != oi_end) { var_t v = oi->second; - if (a.local.find(v) == a.local.end()) + if (!std::binary_search(a.local.begin(), a.local.end(), v)) return (default_instruction_cost(a, i, G, I)); c += 1.0f; @@ -246,7 +248,7 @@ static float assign_cost(const assignment &a, unsigned short int i, while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) != a.local.end() + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); if (byteregs[I[v].byte] == a.global[v]) @@ -256,7 +258,7 @@ static float assign_cost(const assignment &a, unsigned short int i, if (byteregs[0] == REG_A) c -= 0.4f; - else if (OPTRALLOC_IY && byteregs[0] == REG_IYL || byteregs[0] == REG_IYH) + else if ((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) c += 0.1f; } @@ -290,7 +292,7 @@ static float return_cost(const assignment &a, unsigned short int i, if (oi != oi_end) { var_t v = oi->second; - if (a.local.find(v) == a.local.end()) + if (!std::binary_search(a.local.begin(), a.local.end(), v)) return (default_instruction_cost(a, i, G, I)); c += 1.0f; @@ -299,7 +301,7 @@ static float return_cost(const assignment &a, unsigned short int i, while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) != a.local.end() + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); byteregs[I[v].byte] = a.global[v]; @@ -346,7 +348,7 @@ static float call_cost(const assignment &a, unsigned short int i, const G_t &G, if (oi != oi_end) { var_t v = oi->second; - if (a.local.find(v) == a.local.end()) + if (!std::binary_search(a.local.begin(), a.local.end(), v)) return (default_instruction_cost(a, i, G, I)); c += 1.0f; @@ -355,7 +357,7 @@ static float call_cost(const assignment &a, unsigned short int i, const G_t &G, while (++oi != oi_end) { v = oi->second; - c += (a.local.find(v) != a.local.end() + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); byteregs[I[v].byte] = a.global[v]; @@ -411,9 +413,8 @@ static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) { if (!result || !IS_SYMOP(result)) return; - if (!(ic->op == '~' || ic->op == UNARYMINUS || ic->op == '+' || - ic->op == '-' || ic->op == '^' || ic->op == '|' || - ic->op == BITWISEAND)) + if (!(ic->op == UNARYMINUS || ic->op == '+' || ic->op == '-' || + ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND)) return; // Code generation can always handle all other operations. Todo: // Handle ^, |, BITWISEAND and float UNARYMINUS there as well. @@ -486,6 +487,27 @@ static bool operand_in_reg(const operand *o, const i_assignment_t &ia, return (false); } +// Return true, iff the operand is placed in a reg. +template +static bool operand_byte_in_reg(const operand *o, int offset, reg_t r, + const assignment &a, unsigned short int i, + const G_t &G) { + if (!o || !IS_SYMOP(o)) + return (false); + + operand_map_t::const_iterator oi, oi2, oi3, oi_end; + + for (boost::tie(oi, oi_end) = + G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + offset && oi != oi_end; offset--, oi++) + ; + + if (oi == oi_end) + return (false); + + return (a.global[oi->second] == r); +} + // Return true, iff the operand is placed on the stack. template bool operand_on_stack(const operand *o, const assignment &a, @@ -499,6 +521,13 @@ bool operand_on_stack(const operand *o, const assignment &a, if (OP_SYMBOL_CONST(o)->_isparm && !IS_REGPARM(OP_SYMBOL_CONST(o)->etype)) return (true); + if (IS_TRUE_SYMOP(o) && OP_SYMBOL_CONST(o)->onStack) + return (true); + + if (OP_SYMBOL_CONST(o)->nRegs > + 4) // currently all variables > 4 Byte are spilt in ralloc.c. + return (true); + operand_map_t::const_iterator oi, oi_end; for (boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); @@ -528,7 +557,8 @@ static bool operand_is_pair(const operand *o, const assignment &a, if (oi3 != oi_end) return (false); - if (a.global[oi->second] % 2) + if (a.global[oi->second] != REG_C && a.global[oi->second] != REG_E && + a.global[oi->second] != REG_L && a.global[oi->second] != REG_IYL) return (false); if (a.global[oi->second] + 1 != a.global[oi2->second]) return (false); @@ -543,23 +573,23 @@ static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, const i_assignment_t &ia = a.i_assignment; - const operand *const left = IC_LEFT(ic); - const operand *const right = IC_RIGHT(ic); + operand *const left = IC_LEFT(ic); + operand *const right = IC_RIGHT(ic); const operand *const result = IC_RESULT(ic); if (ia.registers[REG_A][1] < 0) return (true); // Register A not in use. + // Some instructions don't touch registers. + if (SKIP_IC2(ic)) + return (true); + bool exstk = (should_omit_frame_ptr || (currFunc && currFunc->stack > 127) || IS_GB); - // std::cout << "Ainst_ok: A = (" << ia.registers[REG_A][0] << ", " << - // ia.registers[REG_A][1] << "), inst " << i << ", " << ic->key << "\n"; - - // Code generator cannot handle variables that are only partially in A. - if (I[ia.registers[REG_A][1]].size > 1 || - ia.registers[REG_A][0] >= 0 && I[ia.registers[REG_A][0]].size > 1) - return (false); + // std::cout << "Ainst_ok at " << G[i].ic->key << ": A = (" << + // ia.registers[REG_A][0] << ", " << ia.registers[REG_A][1] << "), inst " << i + // << ", " << ic->key << "\n"; // Check if the result of this instruction is placed in A. bool result_in_A = operand_in_reg(IC_RESULT(ic), REG_A, ia, i, G); @@ -579,45 +609,166 @@ static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, break; } - // bit instructions do not disturb a. - if (ic->op == BITWISEAND && ifxForOp(IC_RESULT(ic), ic) && - (IS_OP_LITERAL(left) && (!(IS_GB && IS_TRUE_SYMOP(right) || - exstk && operand_on_stack(right, a, i, G)) || - operand_in_reg(right, ia, i, G) && - !operand_in_reg(right, REG_IYL, ia, i, G) && - !operand_in_reg(right, REG_IYH, ia, i, G)) || - IS_OP_LITERAL(right) && - (!(IS_GB && IS_TRUE_SYMOP(left) || - exstk && operand_on_stack(left, a, i, G)) || - operand_in_reg(left, ia, i, G) && - !operand_in_reg(left, REG_IYL, ia, i, G) && - !operand_in_reg(left, REG_IYH, ia, i, G)))) { - operand *const litop = IS_OP_LITERAL(left) ? IC_LEFT(ic) : IC_RIGHT(ic); - for (unsigned int i = 0; i < getSize(operandType(result)); i++) { - unsigned char byte = (ulFromVal(OP_VALUE(litop)) >> (i * 8) & 0xff); - if (byte != 0x00 && byte != 0x01 && byte != 0x02 && byte != 0x04 && - byte != 0x08 && byte != 0x10 && byte != 0x20 && byte != 0x40 && - byte != 0x80) - goto nobit; - } + // sfr access needs to go through a. + if (input_in_A && + (IS_TRUE_SYMOP(left) && IN_REGSP(SPEC_OCLS(OP_SYMBOL(left)->etype)) || + IS_TRUE_SYMOP(right) && IN_REGSP(SPEC_OCLS(OP_SYMBOL(right)->etype)))) + return (false); + + if (ic->op == '^' || ic->op == BITWISEAND || ic->op == '|' || ic->op == '~' || + ic->op == LEFT_OP) // Codegen can handle it all. + return (true); + + if (ic->op == RIGHT_OP && getSize(operandType(result)) == 1 && + IS_OP_LITERAL(right)) + return (true); + + // Can use non-destructive cp on == and < (> might swap operands). + if ((ic->op == EQ_OP || ic->op == '<' && + SPEC_USIGN(getSpec(operandType(left))) && + SPEC_USIGN(getSpec(operandType(right)))) && + getSize(operandType(IC_LEFT(ic))) == 1 && ifxForOp(IC_RESULT(ic), ic) && + operand_in_reg(left, REG_A, ia, i, G) && + (IS_OP_LITERAL(right) || operand_in_reg(right, REG_C, ia, i, G) || + operand_in_reg(right, REG_B, ia, i, G) || + operand_in_reg(right, REG_E, ia, i, G) || + operand_in_reg(right, REG_D, ia, i, G) || + operand_in_reg(right, REG_H, ia, i, G) || + operand_in_reg(right, REG_L, ia, i, G))) + return (true); + + const cfg_dying_t &dying = G[i].dying; + const bool dying_A = result_in_A || + dying.find(ia.registers[REG_A][1]) != dying.end() || + dying.find(ia.registers[REG_A][0]) != dying.end(); + + if ((ic->op == '+' || + ic->op == '-' && !operand_in_reg(right, REG_A, ia, i, G) || + ic->op == UNARYMINUS && !IS_GB) && + getSize(operandType(IC_RESULT(ic))) == 1 && dying_A) return (true); + + if ((ic->op == '+' || + ic->op == '-' && !operand_in_reg(right, REG_A, ia, i, G) || + ic->op == UNARYMINUS && !IS_GB || + ic->op == + '~') && // First byte of input and last byte of output may be in A. + IS_ITEMP(result) && + dying_A && + (IS_ITEMP(left) || IS_OP_LITERAL(left) || + operand_on_stack(left, a, i, G)) && + (!right || IS_ITEMP(right) || IS_OP_LITERAL(right) || + operand_on_stack(right, a, i, G))) { + + if ((operand_byte_in_reg(left, 0, REG_A, a, i, G) || + !operand_in_reg(left, REG_A, ia, i, G)) && + (operand_byte_in_reg(right, 0, REG_A, a, i, G) || + !operand_in_reg(right, REG_A, ia, i, G)) && + (operand_byte_in_reg(result, getSize(operandType(IC_RESULT(ic))) - 1, + REG_A, a, i, G) || + !result_in_A)) + return (true); } -nobit: - const std::set &dying = G[i].dying; + // First two bytes of input may be in A. + if (ic->op == IFX && dying_A && + (getSize(operandType(left)) >= 1 && + operand_byte_in_reg(left, 0, REG_A, a, i, G) || + getSize(operandType(left)) >= 2 && !IS_FLOAT(operandType(left)) && + operand_byte_in_reg(left, 1, REG_A, a, i, G))) + return (true); + + // Can test register via inc / dec. + if (ic->op == IFX && getSize(operandType(left)) == 1 && + (operand_byte_in_reg(left, 0, REG_B, a, i, G) || + operand_byte_in_reg(left, 0, REG_C, a, i, G) || + operand_byte_in_reg(left, 0, REG_D, a, i, G) || + operand_byte_in_reg(left, 0, REG_E, a, i, G) || + operand_byte_in_reg(left, 0, REG_H, a, i, G) || + operand_byte_in_reg(left, 0, REG_L, a, i, G))) + return (true); + + // Plain assignment between registers + if (ic->op == CAST && getSize(operandType(IC_RESULT(ic))) == 1 && + (operand_in_reg(result, REG_B, ia, i, G) || + operand_in_reg(result, REG_C, ia, i, G) || + operand_in_reg(result, REG_D, ia, i, G) || + operand_in_reg(result, REG_E, ia, i, G) || + operand_in_reg(result, REG_A, ia, i, G)) && + (operand_in_reg(result, REG_B, ia, i, G) || + operand_in_reg(result, REG_C, ia, i, G) || + operand_in_reg(result, REG_D, ia, i, G) || + operand_in_reg(result, REG_E, ia, i, G))) + return (true); + + // Any input byte in A is ok, when all operands are registers other than iy. + if (ic->op == CAST && operand_in_reg(right, REG_A, ia, i, G) && + !operand_in_reg(result, REG_A, ia, i, G) && + (operand_in_reg(result, REG_C, ia, i, G) || + operand_in_reg(result, REG_E, ia, i, G) || + operand_in_reg(result, REG_L, ia, i, G))) + return (true); + + // Last byte of output may be in A. + if ((ic->op == GET_VALUE_AT_ADDRESS || + ic->op == CAST && !operand_in_reg(right, REG_A, ia, i, G)) && + IS_ITEMP(result) && + operand_byte_in_reg(result, getSize(operandType(IC_RESULT(ic))) - 1, + REG_A, a, i, G)) + return (true); + + // inc / dec does not affect a. + if ((ic->op == '+' || ic->op == '-') && IS_OP_LITERAL(right) && + ulFromVal(OP_VALUE_CONST(right)) <= 2 && + (getSize(operandType(IC_RESULT(ic))) == 2 && + operand_is_pair(IC_RESULT(ic), a, i, G) || + getSize(operandType(IC_RESULT(ic))) == 1 && + operand_in_reg(result, ia, i, G) && + operand_in_reg(result, ia, i, G))) + return (true); + + if (ic->op == + GET_VALUE_AT_ADDRESS) // Any register can be assigned from (hl) and (iy), + // so we don't need to go through a then. + return (!IS_BITVAR(getSpec(operandType(result))) && + (getSize(operandType(result)) == 1 || + operand_is_pair(left, a, i, G) && + (operand_in_reg(left, REG_L, ia, i, G) && + !ulFromVal(OP_VALUE_CONST(right)) || + operand_in_reg(left, REG_IYL, ia, i, G) && + ulFromVal(OP_VALUE_CONST(right)) <= 127))); + + if (ic->op == '=' && + POINTER_SET(ic) && // Any register can be assigned to (hl) and (iy), so we + // don't need to go through a then. + !(IS_BITVAR(getSpec(operandType(result))) || + IS_BITVAR(getSpec(operandType(right)))) && + (getSize(operandType(right)) == 1 || + operand_is_pair(result, a, i, G) && + (operand_in_reg(result, REG_L, ia, i, G) || + operand_in_reg(result, REG_IYL, ia, i, G)))) + return (true); + + // Code generator mostly cannot handle variables that are only partially in A. + if (operand_in_reg(left, REG_A, ia, i, G) && + getSize(operandType(left)) != 1 || + operand_in_reg(right, REG_A, ia, i, G) && + getSize(operandType(right)) != 1 || + operand_in_reg(result, REG_A, ia, i, G) && + getSize(operandType(result)) != 1) + return (false); + + if (ic->op == '!' && getSize(operandType(left)) <= 2 && dying_A) + return (true); - if (ic->op == GET_VALUE_AT_ADDRESS) - return (result_in_A || !IS_BITVAR(getSpec(operandType(result)))); if (ic->op == '=' && POINTER_SET(ic)) - return (dying.find(ia.registers[REG_A][1]) != dying.end() || - dying.find(ia.registers[REG_A][0]) != dying.end() || - !(IS_BITVAR(getSpec(operandType(result))) || - IS_BITVAR(getSpec(operandType(right))))); + return (dying_A || !(IS_BITVAR(getSpec(operandType(result))) || + IS_BITVAR(getSpec(operandType(right))))); if (1) { // Variable in A is not used by this instruction - if (ic->op == '+' && IS_ITEMP(IC_LEFT(ic)) && IS_ITEMP(IC_RESULT(ic)) && - IS_OP_LITERAL(right) && ulFromVal(OP_VALUE(IC_RIGHT(ic))) == 1 && + if (ic->op == '+' && IS_ITEMP(left) && IS_ITEMP(IC_RESULT(ic)) && + IS_OP_LITERAL(right) && ulFromVal(OP_VALUE_CONST(right)) == 1 && OP_KEY(IC_RESULT(ic)) == OP_KEY(IC_LEFT(ic))) return (true); @@ -646,36 +797,20 @@ static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, if (ic->op == GOTO || ic->op == LABEL) return (true); - if (ic->op == IPUSH && getSize(operandType(IC_LEFT(ic))) <= 2 && - (operand_in_reg(left, REG_A, ia, i, G) || - operand_in_reg(left, REG_B, ia, i, G) && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_C, ia, i, G) && - I[ia.registers[REG_C][1]].byte == 0) || - operand_in_reg(left, REG_D, ia, i, G) && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_E, ia, i, G) && - I[ia.registers[REG_E][1]].byte == 0) || - operand_in_reg(left, REG_H, ia, i, G) && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_L, ia, i, G) && - I[ia.registers[REG_L][1]].byte == 0) || - operand_in_reg(left, REG_IYL, ia, i, G) && - I[ia.registers[REG_IYL][1]].byte == 0 && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_IYH, ia, i, G)))) + if (ic->op == IPUSH) // Can handle anything. return (true); + if (!result_in_A && !input_in_A) return (false); } // Last use of operand in A. - if (input_in_A && - (result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || - dying.find(ia.registers[REG_A][0]) != dying.end())) { + if (input_in_A && dying_A) { if (ic->op != IFX && ic->op != RETURN && !((ic->op == RIGHT_OP || ic->op == LEFT_OP) && - (IS_OP_LITERAL(right) || operand_in_reg(right, REG_A, ia, i, G))) && + (IS_OP_LITERAL(right) || operand_in_reg(right, REG_A, ia, i, G) || + getSize(operandType(IC_RESULT(ic))) == 1 && + ia.registers[REG_B][1] < 0)) && !((ic->op == '=' || ic->op == CAST) && !(IY_RESERVED && POINTER_SET(ic))) && !IS_BITWISE_OP(ic) && !(ic->op == '~') && @@ -761,7 +896,7 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, } bool input_in_HL = input_in_L || input_in_H; - const std::set &dying = G[i].dying; + const cfg_dying_t &dying = G[i].dying; bool dying_L = result_in_L || dying.find(ia.registers[REG_L][1]) != dying.end() || @@ -774,16 +909,61 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, (result_in_H || unused_H || dying_H); #if 0 - if (ic->key == 4) + if (ic->key == 6) { - std::cout << "Result in L: " << result_in_L << ", result in H: " << result_in_H << "\n"; - std::cout << "Unsued L: " << unused_L << ", unused H: " << unused_H << "\n"; - std::cout << "Dying L: " << dying_L << ", dying H: " << dying_H << "\n"; - std::cout << "Result only HL: " << result_only_HL << "\n"; + std::cout << " Result in L: " << result_in_L << ", result in H: " << result_in_H << "\n"; + std::cout << " Unsued L: " << unused_L << ", unused H: " << unused_H << "\n"; + std::cout << " Dying L: " << dying_L << ", dying H: " << dying_H << "\n"; + std::cout << " Result only HL: " << result_only_HL << "\n"; } #endif - if (ic->op == RETURN || ic->op == SEND) + // Due to lack of ex hl, (sp), the generic push code generation fallback + // doesn't work for gbz80, so we need to be able to use hl if we can't just + // push a pair or use a. + if (IS_GB && ic->op == IPUSH && !operand_is_pair(left, a, i, G) && + ia.registers[REG_A][1] >= 0 && + !(getSize(operandType(left)) == 1 && + (operand_in_reg(left, REG_A, ia, i, G) || + operand_in_reg(left, REG_B, ia, i, G) || + operand_in_reg(left, REG_D, ia, i, G) || + operand_in_reg(left, REG_H, ia, i, G)))) + return (false); + + if (IS_GB && !result_only_HL && + (operand_on_stack(result, a, i, G) || operand_on_stack(left, a, i, G) || + operand_on_stack(right, a, i, G))) + return (false); + + if (IS_GB && ic->op == GET_VALUE_AT_ADDRESS && !result_only_HL && + (getSize(operandType(result)) >= 2 || !operand_is_pair(left, a, i, G))) + return (false); + + // For some operations, the gbz80 stack access using hl will trash the value + // there. + if (IS_GB && (ic->op == IPUSH && operand_on_stack(left, a, i, G) || + ic->op == IFX && operand_on_stack(IC_COND(ic), a, i, G))) + return (false); + if (IS_GB && + (operand_on_stack(result, a, i, G) || operand_on_stack(left, a, i, G) || + operand_on_stack(right, a, i, G)) && + (ic->op == RIGHT_OP || ic->op == LEFT_OP || ic->op == '=' || + ic->op == CAST || ic->op == '-' || ic->op == UNARYMINUS || + ic->op == BITWISEAND || ic->op == '|') && + !(result_only_HL && + getSize(operandType(result)) == + 1)) // Size of result needs to be checked after checking ic->op to + // esnure that there is a result operand. + return (false); + + if (IS_GB && ic->op == GET_VALUE_AT_ADDRESS && + !(result_only_HL || getSize(operandType(result)) == 1)) + return (false); + if (IS_GB && POINTER_GET(ic) && + !(result_only_HL || getSize(operandType(right)) == 1)) + return (false); + + if (ic->op == RETURN || ic->op == SEND || ic->op == RECEIVE) return (true); if ((IS_GB || IY_RESERVED) && (IS_TRUE_SYMOP(left) || IS_TRUE_SYMOP(right))) @@ -793,21 +973,38 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, getSize(operandType(IC_RESULT(ic))) > 2) return (false); + // __z88dk_fastcall passes parameter in hl + if (ic->op == PCALL && ic->prev && ic->prev->op == SEND && input_in_HL && + IFFUNC_ISZ88DK_FASTCALL(operandType(IC_LEFT(ic))->next)) + return (false); + + // HL overwritten by result. if (result_only_HL && ic->op == PCALL) return (true); + if (ic->op == '-' && getSize(operandType(result)) == 2 && + IS_TRUE_SYMOP(left) && IS_TRUE_SYMOP(right) && result_only_HL) + return (true); + if (exstk && (operand_on_stack(result, a, i, G) + operand_on_stack(left, a, i, G) + operand_on_stack(right, a, i, G) >= 2) && (result && IS_SYMOP(result) && getSize(operandType(result)) >= 2 || - !result_only_HL)) // Todo: Make this more accurate to get better code - // when using --fomit-frame-pointer + !result_only_HL)) + // Todo: Make this more accurate to get better code when using + // --fomit-frame-pointer return (false); if (exstk && (operand_on_stack(left, a, i, G) || operand_on_stack(right, a, i, G)) && (ic->op == '>' || ic->op == '<')) return (false); + if (ic->op == '+' && getSize(operandType(result)) >= 2 && input_in_HL && + ((exstk ? operand_on_stack(left, a, i, G) : IS_TRUE_SYMOP(left)) && + (ia.registers[REG_L][1] > 0 || ia.registers[REG_H][1] > 0) || + (exstk ? operand_on_stack(right, a, i, G) : IS_TRUE_SYMOP(right)) && + (ia.registers[REG_L][1] > 0 || ia.registers[REG_H][1] > 0))) + return (false); if (ic->op == '+' && getSize(operandType(result)) == 2 && (IS_OP_LITERAL(right) && ulFromVal(OP_VALUE(IC_RIGHT(ic))) <= 3 || @@ -817,7 +1014,7 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, operand_in_reg(result, REG_H, ia, i, G))) return (true); // Uses inc hl. - if (ic->op == '+' && getSize(operandType(result)) == 2 && + if (!IS_GB && ic->op == '+' && getSize(operandType(result)) == 2 && !IS_TRUE_SYMOP(result) && (result_only_HL || operand_in_reg(result, REG_IYL, ia, i, G) && operand_in_reg(result, REG_IYH, ia, i, G)) && @@ -826,7 +1023,21 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, ia.registers[REG_D][1] < 0)) // Can use ld rr, (nn) instead of (hl). return (true); - if (ic->op == '+' && getSize(operandType(result)) >= 2 && + if (!IS_GB && ic->op == '+' && getSize(operandType(result)) == 2 && + IS_TRUE_SYMOP(left) && + (IS_OP_LITERAL(right) && ulFromVal(OP_VALUE(IC_RIGHT(ic))) <= 3 || + IS_OP_LITERAL(left) && ulFromVal(OP_VALUE(IC_LEFT(ic))) <= 3) && + (operand_in_reg(result, REG_C, ia, i, G) && + I[ia.registers[REG_C][1]].byte == 0 && + operand_in_reg(result, REG_B, ia, i, G) || + operand_in_reg(result, REG_E, ia, i, G) && + I[ia.registers[REG_E][1]].byte == 0 && + operand_in_reg(result, REG_D, ia, i, + G))) // Can use ld rr, (nn) followed by inc rr + return (true); + + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS) && + getSize(operandType(result)) >= 2 && (IS_TRUE_SYMOP(result) && !operand_on_stack(result, a, i, G) || (operand_on_stack(left, a, i, G) ? exstk : IS_TRUE_SYMOP(left)) || (operand_on_stack(right, a, i, G) @@ -849,13 +1060,16 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, if (!exstk && !isOperandInDirSpace(IC_LEFT(ic)) && !isOperandInDirSpace(IC_RIGHT(ic)) && !isOperandInDirSpace(IC_RESULT(ic)) && - (ic->op == '-' || ic->op == '<' || ic->op == '>')) + (ic->op == '-' || ic->op == UNARYMINUS || ic->op == '~' || + ic->op == '<' || ic->op == '>')) return (true); if (ic->op == LEFT_OP && getSize(operandType(result)) <= 2 && IS_OP_LITERAL(right) && result_only_HL) return (true); if ((ic->op == LEFT_OP || ic->op == RIGHT_OP) && + (getSize(operandType(result)) <= 1 || !IS_TRUE_SYMOP(result) || + !(IS_GB || IY_RESERVED)) && (!exstk || ((!operand_on_stack(left, a, i, G) || !input_in_HL && result_only_HL) && (!operand_on_stack(right, a, i, G) || !input_in_HL && result_only_HL) && @@ -878,31 +1092,7 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, return (true); if (SKIP_IC2(ic)) return (true); - if (ic->op == IPUSH && input_in_H && - (getSize(operandType(IC_LEFT(ic))) <= 2 || - ia.registers[REG_L][1] > 0 && I[ia.registers[REG_L][1]].byte == 2 && - ia.registers[REG_H][1] > 0 && I[ia.registers[REG_H][1]].byte == 3)) - return (true); - if (ic->op == IPUSH && ic->next && ic->next->op == CALL) - return (true); - if (ic->op == IPUSH && getSize(operandType(left)) == 2 && - (ia.registers[REG_C][1] < 0 && ia.registers[REG_B][1] < 0 || - ia.registers[REG_E][1] < 0 && - ia.registers[REG_D][1] < 0)) // Can use pair other than HL. - return (true); - if (ic->op == IPUSH && getSize(operandType(left)) <= 2 && - (operand_in_reg(left, REG_C, ia, i, G) && - I[ia.registers[REG_C][1]].byte == 0 && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_B, ia, i, G)) || - operand_in_reg(left, REG_E, ia, i, G) && - I[ia.registers[REG_E][1]].byte == 0 && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_D, ia, i, G)) || - operand_in_reg(left, REG_IYL, ia, i, G) && - I[ia.registers[REG_IYL][1]].byte == 0 && - (getSize(operandType(left)) < 2 || - operand_in_reg(left, REG_IYH, ia, i, G)))) + if (ic->op == IPUSH) // Can handle anything. return (true); if (POINTER_GET(ic) && input_in_L && input_in_H && (getSize(operandType(IC_RESULT(ic))) == 1 || !result_in_HL)) @@ -956,21 +1146,10 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, if (ic->op == CALL) return (true); - // HL overwritten by result. - if (result_only_HL && ic->op == PCALL) - return (true); - if (POINTER_GET(ic) && getSize(operandType(IC_RESULT(ic))) == 1 && !IS_BITVAR(getSpec(operandType(result))) && - (operand_in_reg(right, REG_C, ia, i, G) && - I[ia.registers[REG_C][1]].byte == 0 && - operand_in_reg(right, REG_B, ia, i, G) || // Uses ld a, (bc) - operand_in_reg(right, REG_E, ia, i, G) && - I[ia.registers[REG_E][1]].byte == 0 && - operand_in_reg(right, REG_D, ia, i, G) || // Uses ld a, (de) - operand_in_reg(right, REG_IYL, ia, i, G) && - I[ia.registers[REG_IYL][1]].byte == 0 && - operand_in_reg(right, REG_IYH, ia, i, G))) // Uses ld r, 0 (iy) + operand_is_pair(left, a, i, G) && // Use ld a, (dd) or ld r, 0 (iy). + IS_OP_LITERAL(right) && ulFromVal(OP_VALUE_CONST(right)) == 0) return (true); if ((ic->op == '=') && POINTER_SET(ic) && @@ -987,7 +1166,7 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, return (true); #if 0 - if(ic->key == 4) + if(ic->key == 6) { std::cout << "HLinst_ok: L = (" << ia.registers[REG_L][0] << ", " << ia.registers[REG_L][1] << "), H = (" << ia.registers[REG_H][0] << ", " << ia.registers[REG_H][1] << ")inst " << i << ", " << ic->key << "\n"; std::cout << "Result in L: " << result_in_L << ", result in H: " << result_in_H << "\n"; @@ -995,7 +1174,20 @@ static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, } #endif - return (false); + // Replaces former default drop here. + if (ic->op == GET_VALUE_AT_ADDRESS || POINTER_SET(ic) || + ic->op == ADDRESS_OF || ic->op == '*' || + ic->op == JUMPTABLE) // Some operations always use hl. TODO: See if they + // can be changed to save / restore a hl in use or + // use hl only when free. + return (false); + if (exstk && (operand_on_stack(result, a, i, G) || IS_TRUE_SYMOP(result) || + operand_on_stack(left, a, i, G) || IS_TRUE_SYMOP(left) || + operand_on_stack(right, a, i, G) || + IS_TRUE_SYMOP(right))) // hl used as pointer to operand. + return (false); + + return (true); } template @@ -1006,11 +1198,10 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, const i_assignment_t &ia = a.i_assignment; /*if(ic->key == 40) - std::cout << "1IYinst_ok: at (" << i << ", " << ic->key << - ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << - ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", - " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << - "\n";*/ + std::cout << "1IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << + ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" + << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " + << i << ", " << ic->key << "\n";*/ bool exstk = (should_omit_frame_ptr || (currFunc && currFunc->stack > 127)); @@ -1057,6 +1248,9 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, if (unused_IYL && unused_IYH) return (true); // Register IY not in use. + if (SKIP_IC2(ic)) + return (true); + if (exstk && (operand_on_stack(result, a, i, G) || operand_on_stack(left, a, i, G) || operand_on_stack(right, a, i, @@ -1064,7 +1258,26 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, // when using --fomit-frame-pointer return (false); - // Code generator cannot handle variables that are only partially in IY. + if (ic->op == CALL) + return (true); + + if (!result_in_IY && !input_in_IY && + !(IC_RESULT(ic) && isOperandInDirSpace(IC_RESULT(ic))) && + !(IC_RIGHT(ic) && IS_TRUE_SYMOP(IC_RIGHT(ic))) && + !(IC_LEFT(ic) && IS_TRUE_SYMOP(IC_LEFT(ic)))) + return (true); + + // variables partially in IY can be pushed. + if (ic->op == IPUSH && operand_in_reg(left, REG_IYL, ia, i, G) && + operand_in_reg(left, REG_IYH, ia, i, G) && + (I[ia.registers[REG_IYL][1]].byte == 0 && + I[ia.registers[REG_IYH][1]].byte == 1 || + I[ia.registers[REG_IYL][1]].byte == 2 && + I[ia.registers[REG_IYH][1]].byte == 3)) + return (true); + + // Code generator mostly cannot handle variables that are only partially in + // IY. if (unused_IYL ^ unused_IYH) return (false); if (!unused_IYL && I[ia.registers[REG_IYL][1]].size != 2 || @@ -1096,9 +1309,9 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, return (false); #if 0 - if(ic->key == 99) + if(ic->key == 32) { - std::cout << "IYinst_ok: Assignment: "; + std::cout << "A IYinst_ok: Assignment: "; //print_assignment(a); std::cout << "\n"; std::cout << "2IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << "\n"; @@ -1106,7 +1319,8 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, #endif if (result_in_IY && - (ic->op == '=' || + (ic->op == '=' && + !(POINTER_SET(ic) && isOperandInDirSpace(IC_RIGHT(ic))) || ic->op == CAST && getSize(operandType(IC_RESULT(ic))) <= getSize(operandType(IC_RIGHT(ic))) || ic->op == '+')) // todo: More instructions that can write iy. @@ -1114,8 +1328,7 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, // Todo: Multiplication. - if (ic->op == LEFT_OP && result_in_IY && input_in_IY && - IS_VALOP(IC_RIGHT(ic)) && operandLitValue(IC_RIGHT(ic)) < 8) + if (ic->op == LEFT_OP) return (true); if (ic->op == '-' && result_in_IY && input_in_IY && IS_VALOP(IC_RIGHT(ic)) && @@ -1123,24 +1336,15 @@ static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, return (true); #if 0 - if(ic->key == 99) + if(ic->key == 32) { - std::cout << "IYinst_ok: Assignment: "; + std::cout << "B IYinst_ok: Assignment: "; //print_assignment(a); std::cout << "\n"; std::cout << "2IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << "\n"; } #endif - if (SKIP_IC2(ic)) - return (true); - - if (!result_in_IY && !input_in_IY && - !(IC_RESULT(ic) && isOperandInDirSpace(IC_RESULT(ic))) && - !(IC_RIGHT(ic) && IS_TRUE_SYMOP(IC_RIGHT(ic))) && - !(IC_LEFT(ic) && IS_TRUE_SYMOP(IC_LEFT(ic)))) - return (true); - if (!result_in_IY && !input_in_IY && (ic->op == '=' || ic->op == CAST && getSize(operandType(IC_RIGHT(ic))) >= 2 && @@ -1195,8 +1399,6 @@ bool DEinst_ok(const assignment &a, unsigned short int i, const G_t &G, const operand *right = IC_RIGHT(ic); const operand *result = IC_RESULT(ic); - // const std::set &dying = G[i].dying; - if (ic->op == PCALL) return (false); @@ -1216,13 +1418,23 @@ bool DEinst_ok(const assignment &a, unsigned short int i, const G_t &G, operand_in_reg(result, REG_H, ia, i, G))) return (false); - if (ic->op == '+' && getSize(operandType(result)) >= 2) + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS) && + getSize(operandType(result)) >= 4) + return (false); + + if ((ic->op == '-' || ic->op == UNARYMINUS) && + getSize(operandType(result)) >= + 2 && // Stack access requires arithmetic that trashes carry. + (operand_on_stack(result, a, i, G) || operand_on_stack(left, a, i, G) || + operand_on_stack(right, a, i, G))) return (false); - if (ic->op == UNARYMINUS || ic->op == '-' || ic->op == '*') + if (ic->op == '*') return (false); - if (ic->op == '>' || ic->op == '<') + if ((ic->op == '>' || ic->op == '<') && + !SPEC_USIGN(getSpec(operandType(left))) && + !SPEC_USIGN(getSpec(operandType(right)))) return (false); return (true); @@ -1233,14 +1445,15 @@ static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) { iCode *ic = G[i].ic; - ic->rMask = newBitVect(port->num_regs); - ic->rSurv = newBitVect(port->num_regs); + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); - std::set::const_iterator v, v_end; + cfg_alive_t::const_iterator v, v_end; for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) { if (a.global[*v] < 0) continue; ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + if (G[i].dying.find(*v) == G[i].dying.end()) if (!((IC_RESULT(ic) && !POINTER_SET(ic)) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) @@ -1248,14 +1461,6 @@ static void set_surviving_regs(const assignment &a, unsigned short int i, } } -template -static void unset_surviving_regs(unsigned short int i, const G_t &G) { - iCode *ic = G[i].ic; - - freeBitVect(ic->rSurv); - freeBitVect(ic->rMask); -} - template static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, @@ -1269,23 +1474,10 @@ static void assign_operand_for_cost(operand *o, const assignment &a, oi != oi_end; ++oi) { var_t v = oi->second; if (a.global[v] >= 0) { - if (a.global[v] != REG_A && - (a.global[v] != REG_IYL && a.global[v] != REG_IYH || !OPTRALLOC_IY)) { - sym->regs[I[v].byte] = regsZ80 + a.global[v]; - sym->accuse = 0; - sym->isspilt = false; - sym->nRegs = I[v].size; - } else if (a.global[v] == REG_A) { - sym->accuse = ACCUSE_A; - sym->isspilt = false; - sym->nRegs = 0; - sym->regs[I[v].byte] = 0; - } else { - sym->accuse = ACCUSE_IY; - sym->isspilt = false; - sym->nRegs = 0; - sym->regs[I[v].byte] = 0; - } + sym->regs[I[v].byte] = regsZ80 + a.global[v]; + sym->accuse = 0; + sym->isspilt = false; + sym->nRegs = I[v].size; } else { sym->isspilt = true; sym->accuse = 0; @@ -1311,7 +1503,8 @@ static void assign_operands_for_cost(const assignment &a, unsigned short int i, } if (ic->op == SEND && ic->builtinSEND) - assign_operands_for_cost(a, *(adjacent_vertices(i, G).first), G, I); + assign_operands_for_cost( + a, (unsigned short)*(adjacent_vertices(i, G).first), G, I); } // Cost function. @@ -1347,6 +1540,7 @@ static float instruction_cost(const assignment &a, unsigned short int i, case GOTO: case INLINEASM: return (0.0f); + // Exact cost: case '!': case '~': @@ -1376,7 +1570,7 @@ static float instruction_cost(const assignment &a, unsigned short int i, case ADDRESS_OF: case JUMPTABLE: case CAST: - // case RECEIVE: + case RECEIVE: case SEND: case DUMMY_READ_VOLATILE: case CRITICAL: @@ -1384,9 +1578,9 @@ static float instruction_cost(const assignment &a, unsigned short int i, assign_operands_for_cost(a, i, G, I); set_surviving_regs(a, i, G, I); c = dryZ80iCode(ic); - unset_surviving_regs(i, G); ic->generated = false; return (c); + // Inexact cost: default: return (default_instruction_cost(a, i, G, I)); @@ -1398,7 +1592,7 @@ template float weird_byte_order(const assignment &a, const I_t &I) { varset_t::const_iterator vi, vi_end; for (vi = a.local.begin(), vi_end = a.local.end(); vi != vi_end; ++vi) - if (a.global[*vi] % 2 != I[*vi].byte % 2) + if (a.global[*vi] > 0 && (a.global[*vi] - 1) % 2 != I[*vi].byte % 2) c += 8.0f; return (c); @@ -1437,30 +1631,11 @@ template static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) { - // Can check for Ainst_ok() since A only contains 1-byte variables. - if (!G[i].ic->generated && !Ainst_ok(a, i, G, I)) - return (true); - if (local_assignment_insane(a, I, lastvar)) return (true); const i_assignment_t &ia = a.i_assignment; - // Code generator cannot handle variables that are only partially in IY. - if (OPTRALLOC_IY && (ia.registers[REG_IYL][1] >= 0 && - (I[ia.registers[REG_IYL][1]].size != 2 || - I[ia.registers[REG_IYL][1]].byte != 0) || - ia.registers[REG_IYH][1] >= 0 && - (I[ia.registers[REG_IYH][1]].size != 2 || - I[ia.registers[REG_IYH][1]].byte != 1) || - ia.registers[REG_IYL][0] >= 0 && - (I[ia.registers[REG_IYL][0]].size != 2 || - I[ia.registers[REG_IYL][0]].byte != 0) || - ia.registers[REG_IYH][0] >= 0 && - (I[ia.registers[REG_IYH][0]].size != 2 || - I[ia.registers[REG_IYH][0]].byte != 1))) - return (true); - // Can only check for HLinst_ok() in some cases. if (OPTRALLOC_HL && (ia.registers[REG_L][1] >= 0 && ia.registers[REG_H][1] >= 0) && @@ -1470,8 +1645,7 @@ static bool assignment_hopeless(const assignment &a, unsigned short int i, // Can only check for IYinst_ok() in some cases. if (OPTRALLOC_IY && - (ia.registers[REG_IYL][1] >= 0 && ia.registers[REG_IYH][1] >= 0) && - !((ia.registers[REG_IYL][0] >= 0) ^ (ia.registers[REG_IYH][0] >= 0)) && + (ia.registers[REG_IYL][1] >= 0 || ia.registers[REG_IYH][1] >= 0) && !IYinst_ok(a, i, G, I)) return (true); @@ -1504,8 +1678,10 @@ static void get_best_local_assignment_biased( a = *ai_best; std::set::const_iterator vi, vi_end; - for (vi = T[t].alive.begin(), vi_end = T[t].alive.end(); vi != vi_end; ++vi) - a.local.insert(*vi); + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), + a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; } template @@ -1516,9 +1692,9 @@ static float rough_cost_estimate(const assignment &a, unsigned short int i, c += weird_byte_order(a, I); - if (OPTRALLOC_HL && - (ia.registers[REG_L][1] >= 0 && ia.registers[REG_H][1] >= 0) && - !((ia.registers[REG_L][0] >= 0) ^ (ia.registers[REG_H][0] >= 0)) && + if (OPTRALLOC_HL && ia.registers[REG_L][1] >= 0 && + ia.registers[REG_H][1] >= 0 && + ((ia.registers[REG_L][0] >= 0) == (ia.registers[REG_H][0] >= 0)) && !HLinst_ok(a, i, G, I)) c += 8.0f; @@ -1555,11 +1731,13 @@ static float rough_cost_estimate(const assignment &a, unsigned short int i, c += 32.0f; if ((I[*v].byte % 2) && (a.global[*v] == REG_L || a.global[*v] == REG_E || - a.global[*v] == REG_C)) // Try not to reverse bytes. + a.global[*v] == REG_C || + a.global[*v] == REG_IYL)) // Try not to reverse bytes. c += 8.0f; if (!(I[*v].byte % 2) && I[*v].size > 1 && (a.global[*v] == REG_H || a.global[*v] == REG_D || - a.global[*v] == REG_B)) // Try not to reverse bytes. + a.global[*v] == REG_B || + a.global[*v] == REG_IYH)) // Try not to reverse bytes. c += 8.0f; if (I[*v].byte == 0 && I[*v].size > 1 || I[*v].byte == 2 && I[*v].size > 3) { @@ -1615,8 +1793,8 @@ static void extra_ic_generated(iCode *ic) { } } -template -static bool tree_dec_ralloc(T_t &T, const G_t &G, const I_t &I) { +template +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I, SI_t &SI) { bool assignment_optimal; con2_t I2(boost::num_vertices(I)); @@ -1655,37 +1833,29 @@ static bool tree_dec_ralloc(T_t &T, const G_t &G, const I_t &I) { for (unsigned int v = 0; v < boost::num_vertices(I); v++) { symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); if (winner.global[v] >= 0) { - if (winner.global[v] != REG_A && - (winner.global[v] != REG_IYL && winner.global[v] != REG_IYH || - !OPTRALLOC_IY)) { - sym->regs[I[v].byte] = regsZ80 + winner.global[v]; - sym->accuse = 0; - sym->isspilt = false; - sym->nRegs = I[v].size; - } else if (winner.global[v] == REG_A) { - sym->accuse = ACCUSE_A; - sym->isspilt = false; - sym->nRegs = 0; - sym->regs[0] = 0; - } else { - sym->accuse = ACCUSE_IY; - sym->isspilt = false; - sym->nRegs = 0; - sym->regs[I[v].byte] = 0; - } + + sym->regs[I[v].byte] = regsZ80 + winner.global[v]; + sym->accuse = 0; + sym->isspilt = false; + sym->nRegs = I[v].size; } else { for (int i = 0; i < I[v].size; i++) sym->regs[i] = 0; sym->accuse = 0; sym->nRegs = I[v].size; - // spillThis(sym); Leave it to regFix, which can do some spillocation - // compaction. Todo: Use Thorup instead. - sym->isspilt = false; + if (USE_OLDSALLOC) + sym->isspilt = false; // Leave it to Z80RegFix, which can do some + // spillocation compaction. + else + z80SpillThis(sym); } } for (unsigned int i = 0; i < boost::num_vertices(G); i++) - set_surviving_regs(winner, i, G, I); // Never freed. Memory leak? + set_surviving_regs(winner, i, G, I); + + if (!USE_OLDSALLOC) + set_spilt(G, I, SI); return (!assignment_optimal); } @@ -1742,7 +1912,8 @@ void move_parms(void) { } iCode *z80_ralloc2_cc(ebbIndex *ebbi) { - iCode *ic; + eBBlock **const ebbs = ebbi->bbOrder; + const int count = ebbi->count; #ifdef DEBUG_RALLOC_DEC std::cout << "Processing " << currFunc->name << " from " << dstFileName @@ -1754,7 +1925,7 @@ iCode *z80_ralloc2_cc(ebbIndex *ebbi) { con_t conflict_graph; - ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + iCode *ic = create_cfg(control_flow_graph, conflict_graph, ebbi); should_omit_frame_ptr = omit_frame_ptr(control_flow_graph); move_parms(); @@ -1767,9 +1938,7 @@ iCode *z80_ralloc2_cc(ebbIndex *ebbi) { tree_dec_t tree_decomposition; - thorup_tree_decomposition(tree_decomposition, control_flow_graph); - - nicify(tree_decomposition); + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); alive_tree_dec(tree_decomposition, control_flow_graph); @@ -1780,8 +1949,23 @@ iCode *z80_ralloc2_cc(ebbIndex *ebbi) { if (options.dump_graphs) dump_tree_decomposition(tree_decomposition); + guessCounts(ic, ebbi); + + scon_t stack_conflict_graph; + z80_assignment_optimal = - !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph); + !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph, + stack_conflict_graph); + + Z80RegFix(ebbs, count); + + if (USE_OLDSALLOC) + redoStackOffsets(); + else + chaitin_salloc(stack_conflict_graph); // new Chaitin-style stack allocator + + if (options.dump_graphs && !USE_OLDSALLOC) + dump_scon(stack_conflict_graph); return (ic); } diff --git a/src/backend/z80.h b/src/backend/z80.h index 3dd0b285b..d8ee28a49 100644 --- a/src/backend/z80.h +++ b/src/backend/z80.h @@ -1,19 +1,15 @@ /** @file z80/z80.h Common definitions between the z80 and gbz80 parts. */ -#include "common.h" +#include "../common.h" +#include "ralloc.h" #include "gen.h" #include "peep.h" -#include "ralloc.h" #include "support.h" typedef enum { SUB_Z80, - SUB_Z180, - SUB_R2K, - SUB_R3KA, - SUB_GBZ80, - SUB_TLCS90 + SUB_EZ80_Z80, } Z80_SUB_PORT; typedef struct { @@ -23,21 +19,26 @@ typedef struct { int port_back; int reserveIY; int noOmitFramePtr; + int legacyBanking; + int nmosZ80; } Z80_OPTS; extern Z80_OPTS z80_opts; +#define IS_RAB false +#define IS_TLCS90 false +#define IS_Z80N false +#define IS_Z180 false +#define IS_R2K false +#define IS_R2KA false +#define IS_R3KA false +#define IS_GB false #define IS_Z80 (z80_opts.sub == SUB_Z80) -#define IS_Z180 (z80_opts.sub == SUB_Z180) -#define IS_R2K (z80_opts.sub == SUB_R2K) -#define IS_R3KA (z80_opts.sub == SUB_R3KA) -#define IS_RAB (IS_R2K || IS_R3KA) -#define IS_GB (z80_opts.sub == SUB_GBZ80) -#define IS_TLCS90 (z80_opts.sub == SUB_TLCS90) +#define IS_EZ80_Z80 (z80_opts.sub == SUB_EZ80_Z80) #define IY_RESERVED (z80_opts.reserveIY) -#define OPTRALLOC_HL (!IS_GB) -#define OPTRALLOC_IY !(IY_RESERVED || IS_GB) +#define OPTRALLOC_HL 1 +#define OPTRALLOC_IY !(IY_RESERVED) enum { ACCUSE_A = 1, ACCUSE_SCRATCH, ACCUSE_IY }; diff --git a/src/common.h b/src/common.h index 9835479ba..8516a97fb 100644 --- a/src/common.h +++ b/src/common.h @@ -18,9 +18,9 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -------------------------------------------------------------------------*/ -#include #include #include +#include #include #ifndef COMMON_H @@ -38,28 +38,28 @@ extern "C" { #endif -#include "SDCCBBlock.h" -#include "SDCCasm.h" -#include "SDCCast.h" -#include "SDCCbitv.h" -#include "SDCCcflow.h" -#include "SDCCcse.h" -#include "SDCCdflow.h" #include "SDCCglobl.h" -#include "SDCCglue.h" +#include "SDCCmem.h" +#include "SDCCast.h" +#include "SDCCy.h" #include "SDCChasht.h" +#include "SDCCbitv.h" +#include "SDCCset.h" #include "SDCCicode.h" #include "SDCClabel.h" +#include "SDCCBBlock.h" #include "SDCCloop.h" +#include "SDCCcse.h" +#include "SDCCcflow.h" +#include "SDCCdflow.h" #include "SDCClrange.h" -#include "SDCCmem.h" +#include "SDCCptropt.h" #include "SDCCopt.h" +#include "SDCCglue.h" #include "SDCCpeeph.h" -#include "SDCCptropt.h" -#include "SDCCset.h" -#include "SDCCsystem.h" #include "SDCCutil.h" -#include "SDCCy.h" +#include "SDCCasm.h" +#include "SDCCsystem.h" #include "port.h" diff --git a/src/port.h b/src/port.h index f3113099b..debccebd3 100644 --- a/src/port.h +++ b/src/port.h @@ -10,31 +10,40 @@ #include "SDCCpeeph.h" #include "dbuf.h" +#define TARGET_PDK_LIKE false +#define TARGET_PIC_LIKE false +#define TARGET_IS_PIC16 false +#define TARGET_IS_PIC14 false +#define TARGET_IS_STM8 false +#define TARGET_Z80_LIKE true + #define MAX_BUILTIN_ARGS 16 /* definition of builtin functions */ -typedef struct builtins { - char *name; /* name of builtin function */ - char *rtype; /* return type as string : see typeFromStr */ - int nParms; /* number of parms : max 8 */ - char *parm_types[MAX_BUILTIN_ARGS]; /* each parm type as string : see - typeFromStr */ +typedef struct builtins +{ + char *name; /* name of builtin function */ + char *rtype; /* return type as string : see typeFromStr */ + int nParms; /* number of parms : max 8 */ + char *parm_types[MAX_BUILTIN_ARGS]; /* each parm type as string : see typeFromStr */ } builtins; struct ebbIndex; /* pragma structure */ -struct pragma_s { +struct pragma_s +{ const char *name; int id; char deprecated; - int (*func)(int id, const char *name, const char *cp); + int (*func) (int id, const char *name, const char *cp); }; /* defined in SDCClex.lex */ -int process_pragma_tbl(const struct pragma_s *pragma_tbl, const char *s); +int process_pragma_tbl (const struct pragma_s *pragma_tbl, const char *s); /* Processor specific names */ -typedef struct { +typedef struct +{ /** Unique id for this target */ const int id; /** Target name used for -m */ @@ -46,9 +55,10 @@ typedef struct { /** Specific processor for the given target family. specified by -p */ char *processor; - struct { + struct + { /** Pointer to glue function */ - void (*do_glue)(void); + void (*do_glue) (void); /** TRUE if all types of glue functions should be inserted into the file that also defines main. We dont want this in cases like the z80 where the startup @@ -56,10 +66,17 @@ typedef struct { */ bool glue_up_main; /* OR of MODEL_* */ - } general; + int supported_models; + int default_model; + /** return the model string, used as library destination; + port->target is used as model string if get_model is NULL */ + const char *(*get_model) (void); + } + general; /* assembler related information */ - struct { + struct + { /** Command to run and arguments (eg as-z80) */ const char **cmd; /** Alternate macro based form. */ @@ -73,71 +90,80 @@ typedef struct { /* assembler file extension */ const char *file_ext; /** If non-null will be used to execute the assembler. */ - void (*do_assemble)(set *); - } assembler; + void (*do_assemble) (set *); + } + assembler; /* linker related info */ - struct { + struct + { /** Command to run (eg link-z80) */ const char **cmd; /** Alternate macro based form. */ const char *mcmd; /** If non-null will be used to execute the link. */ - void (*do_link)(void); + void (*do_link) (void); /** Extension for object files (.rel, .obj, ...) */ const char *rel_ext; /** 1 if port needs the .lnk file, 0 otherwise */ const int needLinkerScript; const char *const *crt; const char *const *libs; - } linker; + } + linker; /** Default peephole rules */ - struct { + struct + { char *default_rules; - int (*getSize)(lineNode *line); - bitVect *(*getRegsRead)(lineNode *line); - bitVect *(*getRegsWritten)(lineNode *line); - bool (*deadMove)(const char *reg, lineNode *currPl, lineNode *head); - bool (*notUsed)(const char *reg, lineNode *currPl, lineNode *head); - bool (*canAssign)(const char *op1, const char *op2, const char *op3); - bool (*notUsedFrom)(const char *reg, const char *label, lineNode *head); - } peep; + int (*getSize) (lineNode * line); + bitVect *(*getRegsRead) (lineNode * line); + bitVect *(*getRegsWritten) (lineNode * line); + bool (*deadMove) (const char *reg, lineNode * currPl, lineNode * head); + bool (*notUsed) (const char *reg, lineNode * currPl, lineNode * head); + bool (*canAssign) (const char *op1, const char *op2, const char *op3); + bool (*notUsedFrom) (const char *reg, const char *label, lineNode *head); + bool (*symmParmStack) (void); + bool (*canJoinRegs) (const char **regs, char dst[20]); + bool (*canSplitReg) (const char *reg, char dst[][16], int nDst); + } + peep; /** Basic type sizes */ - struct { + struct + { int char_size; int short_size; - unsigned int int_size; + int int_size; int long_size; int longlong_size; - int ptr_size; // near - int fptr_size; // far - int gptr_size; // generic + int near_ptr_size; // __near + int far_ptr_size; // __far + int ptr_size; // generic + int funcptr_size; + int banked_funcptr_size; int bit_size; int float_size; - int max_base_size; - } s; + } + s; /** tags for far, near, xstack, code generic pointers */ - struct { + struct + { int tag_far; int tag_near; int tag_xstack; int tag_code; - } gp_tags; + } + gp_tags; /** memory regions related stuff */ - struct { + struct + { const char *const xstack_name; const char *const istack_name; - /* - * The following 2 items can't be const pointers - * due to ugly implementation in gbz80 target; - * this should be fixed in src/z80/main.c (borutr) - */ - const char *code_name; - const char *data_name; + const char *const code_name; + const char *const data_name; const char *const idata_name; const char *const pdata_name; const char *const xdata_name; @@ -147,31 +173,31 @@ typedef struct { const char *const overlay_name; const char *const post_static_name; const char *const home_name; - const char *const xidata_name; // initialized xdata - const char *const xinit_name; // a code copy of xidata - const char *const const_name; // const data (code or not) - const char *const cabs_name; // const absolute data (code or not) - const char *const xabs_name; // absolute xdata/pdata - const char *const iabs_name; // absolute idata/data - const char *const - initialized_name; // Initialized global (and static local) variables. - const char *const initializer_name; // A code copy of initialized_name (to - // be copied for fast initialization). + const char *const xidata_name; // initialized xdata + const char *const xinit_name; // a code copy of xidata + const char *const const_name; // const data (code or not) + const char *const cabs_name; // const absolute data (code or not) + const char *const xabs_name; // absolute xdata/pdata + const char *const iabs_name; // absolute idata/data + const char *const initialized_name; // Initialized global (and static local) variables. + const char *const initializer_name; // A code copy of initialized_name (to be copied for fast initialization). struct memmap *default_local_map; // default location for auto vars struct memmap *default_globl_map; // default location for globl vars int code_ro; // code space read-only 1=yes - unsigned int - maxextalign; // maximum extended alignment supported, nonnegative power - // of 2 (C11 standard, section 6.2.8). - } mem; + unsigned int maxextalign; // maximum extended alignment supported, nonnegative power of 2 (C11 standard, section 6.2.8). + } + mem; - struct { - void (*genExtraAreaDeclaration)(FILE *, bool); - void (*genExtraAreaLinkOptions)(FILE *); - } extraAreas; + struct + { + void (*genExtraAreaDeclaration) (FILE *, bool); + void (*genExtraAreaLinkOptions) (FILE *); + } + extraAreas; /* stack related information */ - struct { + struct + { /** -1 for grows down (z80), +1 for grows up (mcs51) */ int direction; /** Extra overhead when calling between banks */ @@ -185,22 +211,27 @@ typedef struct { /** 'banked' call overhead. Mild overlap with bank_overhead */ int banked_overhead; - } stack; - - struct { - /** One more than the smallest - mul/div operation the processor can do natively - Eg if the processor has an 8 bit mul, native below is 2 */ - unsigned int muldiv; - /** Size of the biggest shift the port can handle. -1 if port can handle - * shifts of arbitrary size. */ + /** 0 if sp points to last item pushed, 1 if sp points to next location to use */ + int offset; + } + stack; + + struct + { + /** Size of the biggest shift the port can handle. -1 if port can handle shifts of arbitrary size. */ signed int shift; - } support; - struct { - void (*emitDebuggerSymbol)(const char *); - struct { - int (*regNum)(const struct reg_info *); + /* Has support routines for int x int -> long multiplication and unsigned int x unsigned int -> unsigned long multiplication */ + bool has_mulint2long; + } + support; + + struct + { + void (*emitDebuggerSymbol) (const char *); + struct + { + int (*regNum) (const struct reg_info *); bitVect *cfiSame; bitVect *cfiUndef; int addressSize; @@ -208,17 +239,21 @@ typedef struct { int regNumSP; int regNumBP; int offsetSP; - } dwarf; - } debugger; + } + dwarf; + } + debugger; - struct { + struct + { int maxCount; int sizeofElement; int sizeofMatchJump[3]; int sizeofRangeCompare[3]; int sizeofSubtract; int sizeofDispatch; - } jumptableCost; + } + jumptableCost; /** Prefix to add to a C function (eg "_") */ const char *fun_prefix; @@ -227,78 +262,78 @@ typedef struct { First chance to initalise and set any port specific variables. 'port' is set before calling this. May be NULL. */ - void (*init)(void); + void (*init) (void); /** Parses one option + its arguments */ - bool (*parseOption)(int *pargc, char **argv, int *i); + bool (*parseOption) (int *pargc, char **argv, int *i); /** Optional list of automatically parsed options. Should be implemented to at least show the help text correctly. */ OPTION *poptions; /** Initialise port spectific paths */ - void (*initPaths)(void); + void (*initPaths) (void); /** Called after all the options have been parsed. */ - void (*finaliseOptions)(void); - /** Called after the port has been selected but before any - options are parsed. */ - void (*setDefaultOptions)(void); + void (*finaliseOptions) (void); + /** Called after the port has been selected but before any + options are parsed. */ + void (*setDefaultOptions) (void); /** Does the dirty work. */ - void (*assignRegisters)(struct ebbIndex *); + void (*assignRegisters) (struct ebbIndex *); /** Returns the register name of a symbol. Used so that 'reg_info' can be an incomplete type. */ - const char *(*getRegName)(const struct reg_info *reg); + const char *(*getRegName) (const struct reg_info *reg); + + int (*getRegByName) (const char *name); /** Try to keep track of register contents. */ - bool (*rtrackUpdate)(const char *line); + bool (*rtrackUpdate)(const char* line); /* list of keywords that are used by this target (used by lexer) */ char **keywords; /* Write any port specific assembler output. */ - void (*genAssemblerPreamble)(FILE *of); + void (*genAssemblerPreamble) (FILE * of); /* invoked at end assembler file */ - void (*genAssemblerEnd)(FILE *of); + void (*genAssemblerEnd) (FILE * of); /* Write the port specific IVT. If genIVT is NULL or if * it returns zero, default (8051) IVT generation code * will be used. */ - int (*genIVT)(struct dbuf_s *oBuf, symbol **intTable, int intCount); + int (*genIVT) (struct dbuf_s * oBuf, symbol ** intTable, int intCount); - void (*genXINIT)(FILE *of); + void (*genXINIT) (FILE * of); /* Write port specific startup code */ - void (*genInitStartup)(FILE *of); + void (*genInitStartup) (FILE * of); /* parameter passing in register related functions */ - void (*reset_regparms)(void); /* reset the register count */ - int (*reg_parm)( - struct sym_link *, - bool reentrant); /* will return 1 if can be passed in register */ + void (*reset_regparms) (struct sym_link *); /* reset the register count */ + int (*reg_parm) (struct sym_link *, bool reentrant); /* will return 1 if can be passed in register */ /** Process the pragma string 'sz'. Returns 0 if recognised and processed, 1 otherwise. May be NULL. */ - int (*process_pragma)(const char *sz); + int (*process_pragma) (const char *sz); /** Mangles a support function name to reflect the calling model. */ - const char *(*getMangledFunctionName)(const char *szOrginial); + const char *(*getMangledFunctionName) (const char *szOrginial); /** Returns true if the port can multiply the two types nativly without using support functions. */ - bool (*hasNativeMulFor)(iCode *ic, sym_link *left, sym_link *right); + bool (*hasNativeMulFor) (iCode *ic, sym_link *left, sym_link *right); /** Returns true if the port has implemented certain bit manipulation iCodes (RRC, RLC, SWAP, GETHBIT, GETABIT, GETBYTE, GETWORD) */ - bool (*hasExtBitOp)(int op, int size); + bool (*hasExtBitOp) (int op, int size); /** Returns the relative expense of accessing a particular output storage class. Larger values indicate higher expense. */ - int (*oclsExpense)(struct memmap *oclass); + int (*oclsExpense) (struct memmap * oclass); /** If TRUE, then tprintf and !dw will be used for some initalisers */ @@ -310,28 +345,27 @@ typedef struct { bool little_endian; /* condition transformations */ - bool lt_nge; /* transform (a < b) to !(a >= b) */ - bool gt_nle; /* transform (a > b) to !(a <= b) */ - bool le_ngt; /* transform (a <= b) to !(a > b) */ - bool ge_nlt; /* transform (a >= b) to !(a < b) */ - bool ne_neq; /* transform a != b --> ! (a == b) */ - bool eq_nne; /* transform a == b --> ! (a != b) */ + bool lt_nge; /* transform (a < b) to !(a >= b) */ + bool gt_nle; /* transform (a > b) to !(a <= b) */ + bool le_ngt; /* transform (a <= b) to !(a > b) */ + bool ge_nlt; /* transform (a >= b) to !(a < b) */ + bool ne_neq; /* transform a != b --> ! (a == b) */ + bool eq_nne; /* transform a == b --> ! (a != b) */ bool arrayInitializerSuppported; - bool (*cseOk)(iCode *ic, iCode *pdic); - builtins *builtintable; /* table of builtin functions */ - int unqualified_pointer; /* unqualified pointers type is */ - int reset_labelKey; /* reset Label no 1 at the start of a function */ - int globals_allowed; /* global & static locals not allowed ? 0 ONLY - TININative */ - - int num_regs; /* Number of registers handled in the tree-decomposition-based - register allocator in SDCCralloc.hpp */ + bool (*cseOk) (iCode * ic, iCode * pdic); + builtins *builtintable; /* table of builtin functions */ + int unqualified_pointer; /* unqualified pointers type is */ + int reset_labelKey; /* reset Label no 1 at the start of a function */ + int globals_allowed; /* global & static locals not allowed ? 0 ONLY TININative */ + + int num_regs; /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + #define PORT_MAGIC 0xAC32 - /** Used at runtime to detect if this structure has been completly filled in. - */ + /** Used at runtime to detect if this structure has been completely filled in. */ int magic; -} PORT; +} +PORT; extern PORT *port; diff --git a/src/sdccconf.h b/src/sdccconf.h index b07ea5fab..7dbf1b02c 100644 --- a/src/sdccconf.h +++ b/src/sdccconf.h @@ -149,6 +149,6 @@ #define TYPE_WORD short /* Define to 1 if c supports tags in unnamed struct. */ -#define UNNAMED_STRUCT_TAG 1 +#define UNNAMED_STRUCT_TAG 0 #endif /* SDCCCONF_HEADER */ diff --git a/src/util/dbuf.c b/src/util/dbuf.c index 1030ff990..26a6121bf 100644 --- a/src/util/dbuf.c +++ b/src/util/dbuf.c @@ -149,6 +149,25 @@ int dbuf_append(struct dbuf_s *dbuf, const void *buf, size_t len) { return 0; } +/* + * Prepend the buf to the beginning of the buffer. + */ + +int dbuf_prepend(struct dbuf_s *dbuf, const void *buf, size_t len) { + assert(dbuf); + assert(dbuf->alloc); + assert(dbuf->buf); + + if (_dbuf_expand(dbuf, len) != 0) { + memmove(&(((char *)dbuf->buf)[len]), dbuf->buf, dbuf->len); + memcpy(dbuf->buf, buf, len); + dbuf->len += len; + return 1; + } + + return 0; +} + /* * Add '\0' character at the end of the buffer without * count it in the dbuf->len. @@ -171,7 +190,7 @@ const char *dbuf_c_str(struct dbuf_s *dbuf) { * Get the buffer pointer. */ -const void *dbuf_get_buf(struct dbuf_s *dbuf) { +const void *dbuf_get_buf(const struct dbuf_s *dbuf) { assert(dbuf != NULL); assert(dbuf->alloc != 0); assert(dbuf->buf != NULL); @@ -183,7 +202,7 @@ const void *dbuf_get_buf(struct dbuf_s *dbuf) { * Get the buffer length. */ -size_t dbuf_get_length(struct dbuf_s *dbuf) { +size_t dbuf_get_length(const struct dbuf_s *dbuf) { assert(dbuf != NULL); assert(dbuf->alloc != 0); assert(dbuf->buf != NULL); diff --git a/src/util/dbuf.h b/src/util/dbuf.h index 74f285bde..135242a93 100644 --- a/src/util/dbuf.h +++ b/src/util/dbuf.h @@ -45,8 +45,9 @@ int dbuf_init(struct dbuf_s *dbuf, size_t size); int dbuf_is_initialized(struct dbuf_s *dbuf); int dbuf_set_length(struct dbuf_s *dbuf, size_t size); int dbuf_append(struct dbuf_s *dbuf, const void *buf, size_t len); -const void *dbuf_get_buf(struct dbuf_s *dbuf); -size_t dbuf_get_length(struct dbuf_s *dbuf); +int dbuf_prepend(struct dbuf_s *dbuf, const void *buf, size_t len); +const void *dbuf_get_buf(const struct dbuf_s *dbuf); +size_t dbuf_get_length(const struct dbuf_s *dbuf); const char *dbuf_c_str(struct dbuf_s *dbuf); int dbuf_trim(struct dbuf_s *dbuf); void *dbuf_detach(struct dbuf_s *dbuf); diff --git a/src/util/dbuf_string.c b/src/util/dbuf_string.c index 1bd9fde50..bb3060067 100644 --- a/src/util/dbuf_string.c +++ b/src/util/dbuf_string.c @@ -43,6 +43,19 @@ int dbuf_append_str(struct dbuf_s *dbuf, const char *str) { return 0; } +/* + * Prepend string to the beginning of the buffer. + * The buffer is null terminated. + */ + +int dbuf_prepend_str(struct dbuf_s *dbuf, const char *str) { + size_t len; + assert(str != NULL); + + len = strlen(str); + return (dbuf_prepend(dbuf, str, len)); +} + /* * Append single character to the end of the buffer. * The buffer is null terminated. @@ -59,6 +72,18 @@ int dbuf_append_char(struct dbuf_s *dbuf, char chr) { return 0; } +/* + * Prepend single character to the end of the buffer. + * The buffer is null terminated. + */ + +int dbuf_prepend_char(struct dbuf_s *dbuf, char chr) { + char buf[2]; + buf[0] = chr; + buf[1] = '\0'; + return (dbuf_prepend_str(dbuf, buf)); +} + /* * Calculate length of the resulting formatted string. * @@ -119,9 +144,10 @@ static int calc_result_length(const char *format, va_list args) { make the buffer wide enough to cover the gross case. */ total_width += 307; break; - case 's': - total_width += strlen(va_arg(ap, char *)); - break; + case 's': { + const char *p = va_arg(ap, char *); + total_width += strlen(p ? p : "(null)"); + } break; case 'p': case 'n': (void)va_arg(ap, char *); diff --git a/src/util/dbuf_string.h b/src/util/dbuf_string.h index dc7da6afd..76c06ec34 100644 --- a/src/util/dbuf_string.h +++ b/src/util/dbuf_string.h @@ -52,7 +52,9 @@ extern "C" { #endif int dbuf_append_str(struct dbuf_s *dbuf, const char *str); +int dbuf_prepend_str(struct dbuf_s *dbuf, const char *str); int dbuf_append_char(struct dbuf_s *dbuf, char chr); +int dbuf_prepend_char(struct dbuf_s *dbuf, char chr); int dbuf_vprintf(struct dbuf_s *dbuf, const char *format, va_list args); int dbuf_printf(struct dbuf_s *dbuf, const char *format, ...) ATTRIBUTE_PRINTF(2, 3); diff --git a/src/version.h b/src/version.h index 0fd070d10..37fea32ba 100644 --- a/src/version.h +++ b/src/version.h @@ -4,8 +4,8 @@ // TODO: generate this with CMake from Git #define KCC_VERSION_HI 4 -#define KCC_VERSION_LO 0 -#define KCC_VERSION_P 4 -#define KCC_VERSION_STR "4.0.4" +#define KCC_VERSION_LO 1 +#define KCC_VERSION_P 0 +#define KCC_VERSION_STR "4.1.0" #endif