From: Robert Pengelly Date: Wed, 20 May 2026 05:56:31 +0000 (+0100) Subject: Initial commit X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=b9a30c8adc63baba259677263b8b011059f9ecf9;p=scc.git Initial commit --- b9a30c8adc63baba259677263b8b011059f9ecf9 diff --git a/EXTENSIONS.md b/EXTENSIONS.md new file mode 100644 index 0000000..e01b57a --- /dev/null +++ b/EXTENSIONS.md @@ -0,0 +1,43 @@ +## Supported Extensions + +This compiler supports a subset of compiler-specific extensions. +These extensions are available when `-pedantic` is not specified. + +### `__asm__` (Inline Assembly) + +The __asm__ keyword allows the insertion of raw assembly +instructions into the output file. + +See `__asm__` Inline Assembly Syntax below for more details. + +### `__inline__` +A synonym for `inline`. + +### `__restrict__` +A synonym for `restrict`. + +## Pragma Directives + +All `#pragma` directives are currently ignored by the compiler. They are +reserved for future compiler-specific features. + +## `__asm__` Inline Assembly Syntax + +This directive is a very limited GNU-style inline assembly extension. +It allows hand-written assembly to be inserted directly into the +generated assembly output. + +As Intel syntax is the primary target, arguments are written +left-to-right rather than right-to-left. + +Registers should begin with `%%`, as a single `%` specifies an +argument index to inject. + +`outb` and `outw` are special cases and should use operands in their +natural left-to-right order. + +Usage examples: + +```c +__asm__ ("mov %%eax, %0" : : "N" (port)); +__asm__ ("outb %0, %1" : : "a" (val), "d" (port)); diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..f50ef62 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile.unix b/Makefile.unix new file mode 100755 index 0000000..eeb7949 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,30 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -std=c90 + +CC_SRC := $(wildcard $(SRCDIR)/*.c) +VERSION := $(shell date '+%Y%m%d') + +ifeq ($(OS), Windows_NT) +all: scc.exe + +scc.exe: $(CC_SRC) + + $(CC) $(CFLAGS) -DVERSION=$(VERSION) -o $@ $^ +else +all: scc + +scc: $(CC_SRC) + + $(CC) $(CFLAGS) -DVERSION=$(VERSION) -o $@ $^ +endif + +clean: + + if [ -f scc.exe ]; then rm -rf scc.exe; fi + if [ -f scc ]; then rm -rf scc; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100755 index 0000000..f584116 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,21 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -std=c90 + +CC_SRC := $(wildcard $(SRCDIR)/*.c) + +all: scc.exe + +scc.exe: $(CC_SRC) + + $(CC) $(CFLAGS) -o $@ $^ + +clean: + + if exist scc.exe ( del /q scc.exe ) + if exist scc ( del /q scc ) diff --git a/Makefile.wat b/Makefile.wat new file mode 100755 index 0000000..d7c8fb9 --- /dev/null +++ b/Makefile.wat @@ -0,0 +1,31 @@ +#****************************************************************************** +# @file Makefile.wat +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CPP_DIR := $(SRCDIR)/cpp +CC1_DIR := $(SRCDIR)/cc1 + +CPP_SRC := $(wildcard $(CPP_DIR)/*.c) +CC1_SRC := $(wildcard $(CC1_DIR)/*.c) + +all: scc-cpp.exe scc-cc1.exe + +scc-cpp.exe: $(CPP_SRC) + + wcl -fe=$@ $(subst /,\,$^) + +scc-cc1.exe: $(CC1_SRC) + + wcl -fe=$@ $(subst /,\,$^) + +clean: + + for /r %%f in (*.obj) do ( if exist %%f ( del /q %%f ) ) + + if exist scc-cpp.exe ( del /q scc-cpp.exe ) + if exist scc-cc1.exe ( del /q scc-cc1.exe ) + + if exist scc-cpp ( del /q scc-cpp ) + if exist scc-cc1 ( del /q scc-cc1 ) diff --git a/README.md b/README.md new file mode 100755 index 0000000..9b73769 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +## About + + This project provides a C preprocessor and a freestanding C90 + compiler with select extensions from later standards and + compiler-specific extensions. + + See the `Language Support` section below. + + It is designed for Intel 386 and is intended for use in environments + without a host operating system (e.g., kernels, embedded systems, or + bare-metal hobbyist projects). + + Note: This is a freestanding implementation. It provides the C + language translation but does not include a standard library, + startup routines (crt0), or linker scripts. Users must + provide these components to generate a functional executable. + +## Limitations + + This project is currently in early development. Not all GNU + extensions are supported. See the `Language Support` section + below and `EXTENSIONS.md` for details. + +## Language Support + + Preprocessing: + + Supports /* ... */ (block). + + If -pedantic is not specified, then + // (single-line) comments are also supported. + + Supports #pragma once. + + Macro token pasting (##) and stringification (#) operators. + + If -pedantic is not specified, selected preprocessor + extensions are enabled: + + `C99 Extensions`: + + Variadic macros (__VA_ARGS__). + + Compilation: + + All #pragma directives are currently ignored. There are + no immediate plans to implement specific pragma support, + but they are reserved for future compiler-specific features. + + Standard C90: Core language features and scope rules. + + By default, selected extensions beyond C90 are enabled: + + `C99 Extensions`: + + inline + restrict + long long + + `Compiler-Specific Extensions`: + + If -pedantic is not specified, then support for selected + compiler extensions. + + See `EXTENSIONS.md` for a list of supported features. + + Support for 64-bit long types on 32-bit targets via the + -mlong64 flag. + + Note: When building with a 32-bit host compiler or MSVC, + a bootstrap build is recommended as scc uses + long in various places. + + Scoping: Strict C90—all local variable declarations must appear at + the beginning of a block. + + Wide character constants (L'a') and wide string literals (L"...") + are represented as 16-bit values. Define wchar_t as a compatible + 16-bit type (e.g., unsigned short). Programs that define wchar_t + incompatibly will have implementation-defined or incorrect + behaviour. + +## Usage + + To preprocess the input: + scc -E -o example.i example.c + + To transform the input to assembly: + scc -S -o example.s example.c + + To transform the input to an object: + scc -o example.o example.c + + The macro __STDC__ is defined when -std=c90 + is specified. + + +## License + + All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/scc.git + +## Building + + BSD: + + Make sure you have gcc and gmake installed then run gmake -f Makefile.unix. + + Linux: + + Make sure you have gcc and make installed then run make -f Makefile.unix. + + macOS: + + Make sure you have xcode command line tools installed then run + make -f Makefile.unix. + + Windows: + + Make sure you have mingw installed and the location within your PATH variable + then run mingw32-make.exe -f Makefile.w32. diff --git a/cc.c b/cc.c new file mode 100755 index 0000000..c8978fe --- /dev/null +++ b/cc.c @@ -0,0 +1,144 @@ +/****************************************************************************** + * @file cc.c + *****************************************************************************/ +#include +#include +#include + +#include "cc.h" +#include "expr.h" +#include "int64.h" +#include "lex.h" +#include "lib.h" +#include "parse.h" +#include "pp.h" +#include "report.h" +#include "token.h" + +struct cc_state *state = 0; +const char *program_name = 0; + +static void cleanup (void) { + + if (state->ofp && state->ofp != stdout) { fclose (state->ofp); } + if (state->ifp && state->ifp != stdin) { fclose (state->ifp); } + + if (get_error_count () > 0) { + + if (state->ofile) { + remove (state->ofile); + } + + } + +} + +int main (int argc, char **argv) { + + char *p, *root; + + if (argc && *argv) { + + program_name = *argv; + + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } + + } + + atexit (cleanup); + lex_init (); + + state = xmalloc (sizeof (*state)); + parse_args (argc, argv, 1); + + if (!state->ifile) { + + report_at (program_name, 0, REPORT_ERROR, "no input file specified"); + return EXIT_FAILURE; + + } + + if (!state->ofile) { + + char *ifile = (char *) state->ifile, *temp; + + if (strcmp (ifile, "-") == 0) { + + report_at (program_name, 0, REPORT_ERROR, "-o needed when input is from standard input"); + return EXIT_FAILURE; + + } + + /*if ((p = strrchr (ifile, '/')) || (p = strrchr (ifile, '\\'))) { + ifile = (p + 1); + }*/ + + if ((p = strrchr (ifile, '.'))) { + + temp = xmalloc (p - ifile + 3); + + if (state->mode == CC_MODE_COMPILE) { + sprintf (temp, "%.*s.s", (int) (p - ifile), ifile); + } else { + sprintf (temp, "%.*s.i", (int) (p - ifile), ifile); + } + + } else { + + if ((p = strrchr (state->ifile, '/')) || (p = strrchr (state->ifile, '\\'))) { + + unsigned int len = p - state->ifile; + + root = xmalloc (len + 2); + sprintf (root, "%.*s/", (int) len, state->ifile); + + add_include_path (root); + free (root); + + } + + temp = xmalloc (strlen (ifile) + 3); + + if (state->mode == CC_MODE_COMPILE) { + sprintf (temp, "%s.s", ifile); + } else { + sprintf (temp, "%s.i", ifile); + } + + } + + state->ofile = temp; + + } + + state->ofp = stdout; + + if (state->ofile) { + + if (!(state->ofp = fopen (state->ofile, "w"))) { + return EXIT_FAILURE; + } + + } + + if (preprocess_init ()) { + return EXIT_FAILURE; + } + + if (state->mode == CC_MODE_PREPROCESS) { + + preprocess_file (state->ifile); + return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS); + + } + + if (init_tokenizer (state->ifile)) { + return EXIT_FAILURE; + } + + compile_translation_unit (); + return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS); + +} diff --git a/cc.h b/cc.h new file mode 100755 index 0000000..791689c --- /dev/null +++ b/cc.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * @file cc.h + *****************************************************************************/ +#ifndef _CC_H +#define _CC_H + +#include + +#include "list.h" +#include "stdint.h" +#include "vector.h" + +struct cc_state { + + const char *ifile, *ofile; + FILE *ifp, *ofp; + + struct list *pplist; + struct vector vec_hist; + + int traditional_linemarkers; + int no_linemarkers; + int no_leading_underscore; + + int pedantic; + int mode; + int std; + int long64; + int syntax; + + long version; + +}; + +#define CC_MODE_COMPILE 0 +#define CC_MODE_PREPROCESS 1 + +extern struct cc_state *state; +extern const char *program_name; + +#define ASM_SYNTAX_MASM (1 << 0) +#define ASM_SYNTAX_INTEL (1 << 1) +#define ASM_SYNTAX_ATT (1 << 2) +#define ASM_SYNTAX_NASM (1 << 3) + +#endif /* _CC_H */ diff --git a/cstr.c b/cstr.c new file mode 100755 index 0000000..d197860 --- /dev/null +++ b/cstr.c @@ -0,0 +1,69 @@ +/****************************************************************************** + * @file cstr.c + *****************************************************************************/ +#include +#include + +#include "cstr.h" + +extern void *xrealloc (void *__ptr, unsigned int __size); + +static void cstr_realloc (CString *cstr, int new_size) { + + int size = cstr->size_allocated; + + if (size < 8) { + size = 8; + } + + while (size < new_size) { + size *= 2; + } + + cstr->data = xrealloc (cstr->data, size); + cstr->size_allocated = size; + +} + +void cstr_ccat (CString *cstr, int ch) { + + int size = cstr->size + 1; + + if (size > cstr->size_allocated) { + cstr_realloc (cstr, size); + } + + ((unsigned char *) cstr->data)[size - 1] = ch; + cstr->size = size; + +} + +void cstr_cat (CString *cstr, const char *str, int len) { + + int size; + + if (len <= 0) { + len = strlen (str) + 1 + len; + } + + size = cstr->size + len; + + if (size > cstr->size_allocated) { + cstr_realloc (cstr, size); + } + + memmove (((unsigned char *) cstr->data) + cstr->size, str, len); + cstr->size = size; + +} + +void cstr_new (CString *cstr) { + memset (cstr, 0, sizeof (CString)); +} + +void cstr_free (CString *cstr) { + + free (cstr->data); + cstr_new (cstr); + +} diff --git a/cstr.h b/cstr.h new file mode 100755 index 0000000..4301ece --- /dev/null +++ b/cstr.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * @file cstr.h + *****************************************************************************/ +#ifndef _CSTR_H +#define _CSTR_H + +typedef struct { + + int size, size_allocated; + void *data; + +} CString; + +void cstr_ccat (CString *cstr, int ch); +void cstr_cat (CString *cstr, const char *str, int len); + +void cstr_new (CString *cstr); +void cstr_free (CString *cstr); + +#endif /* _CSTR_H */ diff --git a/eval.c b/eval.c new file mode 100755 index 0000000..6e49bb0 --- /dev/null +++ b/eval.c @@ -0,0 +1,971 @@ +/****************************************************************************** + * @file eval.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "cstr.h" +#include "eval.h" +#include "lex.h" +#include "lib.h" +#include "macro.h" +#include "report.h" +#include "stdint.h" + +static uint64_t eval_expr (uint64_t lhs, char *start, char **pp, int outer_prec, int brackets); + +static int hex_value (int ch) { + + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } + + ch = tolower ((unsigned char) ch); + + if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } + + return -1; + +} + +static uint64_t eval_char_constant (char *start, char **pp) { + + char *caret = *pp; + uint64_t value = 0, ch = 0; + int count = 0; + + (*pp)++; + + while (!is_end_of_line[(int) **pp] && **pp != '\'') { + + if (**pp == '\\') { + + (*pp)++; + + switch (**pp) { + + case 'a': + + ch = '\a'; + + (*pp)++; + break; + + case 'b': + + ch = '\b'; + + (*pp)++; + break; + + case 'f': + + ch = '\f'; + + (*pp)++; + break; + + case 'n': + + ch = '\n'; + + (*pp)++; + break; + + case 'r': + + ch = '\r'; + + (*pp)++; + break; + + case 't': + + ch = '\t'; + + (*pp)++; + break; + + case 'v': + + ch = '\v'; + + (*pp)++; + break; + + case '\\': + + ch = '\\'; + + (*pp)++; + break; + + case '\'': + + ch = '\''; + + (*pp)++; + break; + + case '"': + + ch = '"'; + + (*pp)++; + break; + + case '0': case '1': + case '2': case '3': + case '4': case '5': + case '6': case '7': { + + int i = 0; + ch = 0; + + while (i < 3 && **pp >= '0' && **pp <= '7') { + + ch = (ch * 8) + (*((*pp)++) - '0'); + i++; + + } + + break; + + } + + case 'x': { + + int h, any = 0; + ch = 0; + + (*pp)++; + + while ((h = hex_value ((unsigned char) **pp)) >= 0) { + + ch = (ch * 16) + h; + + (*pp)++; + any = 1; + + } + + if (!any) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp - 1, "\\x used with no following hex digits"); + } + + break; + + + } + + case '\0': + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "missing terminating ' character"); + return value; + + default: + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "unknown escape sequence"); + + ch = (unsigned char) **pp; + (*pp)++; + + break; + + } + + } else { + ch = (unsigned char) *((*pp)++); + } + + value = (value << 8) | (ch & 0xff); + count++; + + } + + if (**pp != '\'') { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing terminating ' character"); + } else { + (*pp)++; + } + + if (count > 1) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "multi-character character constant"); + } + + return value; + +} + +static uint64_t eval_unary (uint64_t lhs, char *start, char **pp) { + + char *caret; + *pp = skip_whitespace (*pp); + + if (**pp == '\'') { + return eval_char_constant (start, pp); + } + + if (isdigit ((int) **pp)) { + + uint64_t temp, temp2; + int ch, i; + + if ((*pp)[0] == '0' && tolower ((int) (*pp)[1]) == 'x') { + + unsigned int base = 16; + *pp += 2; + + while (isxdigit ((int) **pp)) { + + temp = lhs * base; + ch = *((*pp)++); + + if (ch >= '0' && ch <= '9') { + temp2 = ch - '0'; + } else { + temp2 = (ch & 0xdf) - ('A' - 10); + } + + lhs = temp + temp2; + + } + + } else if ((*pp)[0] == '0') { + + unsigned int base = 8; + + while (isdigit ((int) **pp)) { + + temp = lhs * base; + lhs = (temp + (*((*pp)++) - '0')); + + } + + } else { + + unsigned int base = 10; + + while (isdigit ((int) **pp)) { + + temp = lhs * base; + lhs = (temp + (*((*pp)++) - '0')); + + } + + } + + caret = *pp; + + if (**pp == 'L') { + + (*pp)++; + + if (**pp == 'L') { + (*pp)++; + } + + if (**pp == 'U') { + (*pp)++; + } + + } else if (**pp == 'U') { + + (*pp)++; + + for (i = 0; i < 2; i++) { + + if (**pp == 'L') { + (*pp)++; + } + + } + + } + + if (is_name_beginner ((int) **pp)) { + + while (is_name_part ((int) **pp)) { + (*pp)++; + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "invalid suffix \"%.*s\" on integer constant", (int) (*pp - caret), caret); + + } + + return lhs; + + } + + if (is_name_beginner ((int) **pp)) { + + char *sname, *temp; + + struct hashtab_name *key; + struct macro *m; + + caret = *pp; + + while (is_name_part ((int) *caret)) { + caret++; + } + + if ((caret - *pp) == 7 && memcmp (*pp, "defined", 7) == 0) { + + caret = skip_whitespace (caret); + *pp = caret; + + if (*caret == '(') { + + caret = skip_whitespace (caret + 1); + *pp = caret; + + while (!is_end_of_line[(int) *caret]) { + + if (isspace ((int) *caret) || *caret == ')') { break; } + caret++; + + } + + sname = xstrndup (*pp, caret - *pp); + caret = skip_whitespace (caret); + + if (*caret != ')') { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' after \"defined\""); + } + + } else { + sname = xstrndup (*pp, caret - *pp); + } + + if (*caret == ')') { caret++; } + *pp = skip_whitespace (caret); + + lhs = !(find_macro (sname) == 0); + return lhs; + + } + + sname = xstrndup (*pp, caret - *pp); + *pp = skip_whitespace (caret); + + if ((key = find_macro (sname))) { + + free (sname); + + if ((m = get_macro (key))) { + + char *line = (temp = process_macro (start, pp, m)); + + lhs = eval_unary (lhs, line, &temp); + free (line); + + return lhs; + + } + + } + + free (sname); + return lhs; + + } + + if (**pp == '!') { + + uint64_t temp = 0; + + *pp = skip_whitespace (*pp + 1); + temp = eval_unary (temp, start, pp); + + return (temp == 0); + + } + + if (**pp == '~') { + + int flip_bits = 0; + + while (**pp == '~') { + + flip_bits = !flip_bits; + *pp = skip_whitespace (*pp + 1); + + } + + lhs = eval_unary (lhs, start, pp); + + if (flip_bits) { + lhs = ~lhs; + } + + return lhs; + + } + + if (**pp == '-') { + + int sign = 1; + + while (**pp == '-' || **pp == '+') { + + if (**pp == '-') { sign = !sign; } + *pp = skip_whitespace (*pp + 1); + + } + + lhs = eval_unary (lhs, start, pp); + + if (!sign) { + lhs = -lhs; + } + + return lhs; + + } + + if (**pp == '(') { + + char *new_line, *temp; + int depth = 0; + + CString cstr; + cstr_new (&cstr); + + caret = (*pp)++; + cstr_cat (&cstr, start, *pp - start); + + while (!is_end_of_line[(int) **pp]) { + + if (**pp == '(') { + + cstr_ccat (&cstr, *((*pp)++)); + + depth++; + continue; + + } + + if (**pp == ')') { + + if (depth > 0) { + + cstr_ccat (&cstr, *((*pp)++)); + + depth--; + continue; + + } + + break; + + } + + cstr_ccat (&cstr, *((*pp)++)); + + } + + if (**pp == ')') { + cstr_ccat (&cstr, *((*pp)++)); + } + + cstr_cat (&cstr, *pp, strlen (*pp)); + cstr_ccat (&cstr, '\0'); + + new_line = temp = xstrdup (cstr.data); + temp += (caret - start) + 1; + + lhs = eval_unary (lhs, new_line, &temp); + lhs = eval_expr (lhs, new_line, &temp, 14, 1); + + if (*(temp = skip_whitespace (temp)) != ')') { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' in expression"); + } + + free (new_line); + + /*caret = (*pp)++; + + lhs = eval_unary (lhs, start, pp); + lhs = eval_expr (lhs, start, pp, 15, 1); + + if (*(*pp = skip_whitespace (*pp)) != ')') { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing ')' in expression"); + }*/ + + return lhs; + + } + + return -1; + +} + +#define OP_MUL 0 +#define OP_DIV 1 +#define OP_MOD 2 +#define OP_PLUS 3 +#define OP_MINUS 4 +#define OP_LT 5 +#define OP_GT 6 +#define OP_LTEQ 7 +#define OP_GTEQ 8 +#define OP_EQEQ 9 +#define OP_NOTEQ 11 +#define OP_AND 12 +#define OP_XOR 13 +#define OP_OR 14 +#define OP_ANDAND 15 +#define OP_OROR 16 +#define OP_LSHIFT 17 +#define OP_RSHIFT 18 +#define OP_QUEST 19 +#define OP_MAX 20 + +struct op { + + char *word; + int kind; + +}; + +static struct op *get_op (char **pp) { + + static struct op kws[] = { + + { "<=", OP_LTEQ }, + { ">=", OP_GTEQ }, + { "==", OP_EQEQ }, + { "!=", OP_NOTEQ }, + { "&&", OP_ANDAND }, + { "||", OP_OROR }, + { "<<", OP_LSHIFT }, + { ">>", OP_RSHIFT }, + + { "*", OP_MUL }, + { "/", OP_DIV }, + { "%", OP_MOD }, + { "+", OP_PLUS }, + { "-", OP_MINUS }, + { "<", OP_LT }, + { ">", OP_GT }, + { "&", OP_AND }, + { "^", OP_XOR }, + { "|", OP_OR }, + { "?", OP_QUEST } + + }; + + struct op *kw; + unsigned int i; + + for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) { + + kw = &kws[i]; + + if (strncmp (*pp, kw->word, strlen (kw->word)) == 0) { + + *pp += strlen (kw->word); + return kw; + + } + + } + + return 0; + +} + +static int get_prec (int kind) { + + switch (kind) { + + case OP_MUL: case OP_DIV: case OP_MOD: + + return 3; + + case OP_PLUS: case OP_MINUS: + + return 4; + + case OP_LSHIFT: case OP_RSHIFT: + + return 5; + + case OP_LT: case OP_GT: case OP_LTEQ: case OP_GTEQ: + + return 6; + + case OP_EQEQ: case OP_NOTEQ: + + return 7; + + case OP_AND: + + return 8; + + case OP_XOR: + + return 9; + + case OP_OR: + + return 10; + + case OP_ANDAND: + + return 11; + + case OP_OROR: + + return 12; + + case OP_QUEST: + + return 13; + + default: + + break; + + } + + return 100; + +} + +static uint64_t eval_expr (uint64_t lhs, char *start, char **pp, int outer_prec, int brackets) { + + struct op *op = 0, *op2 = 0; + int prec, look_ahead; + + char *caret; + uint64_t rhs; + + for (;;) { + + *pp = skip_whitespace (*pp); + + if (brackets && **pp == ')') { + return lhs; + } + + if (is_end_of_line[(int) **pp]) { + break; + } + + if (!(op = get_op (pp)) || (prec = get_prec (op->kind)) >= outer_prec) { + + if (op) { *pp -= strlen (op->word); } + + if (outer_prec == 15) { + break; + } + + return lhs; + + } + + *pp = skip_whitespace (*pp); + + if (is_end_of_line[(int) **pp]) { + break; + } + + rhs = 0; + + if (op->kind == OP_QUEST) { + + uint64_t left = 0, right = 0; + + left = eval_unary (left, start, pp); + left = eval_expr (left, start, pp, 14, brackets); + + *pp = skip_whitespace (*pp); + assert (**pp == ':'); + *pp = skip_whitespace (*pp + 1); + + right = eval_unary (right, start, pp); + right = eval_expr (right, start, pp, 14, brackets); + + if (lhs != 0) { + lhs = left; + } else { + lhs = right; + } + + continue; + + } + + caret = (*pp = skip_whitespace (*pp)); + + if (is_end_of_line[(int) **pp]) { + break; + } + + rhs = eval_unary (rhs, start, pp); + + if (*pp == caret) { + break; + } + + for (;;) { + + *pp = skip_whitespace (*pp); + + if (is_end_of_line[(int) **pp] || (brackets && **pp == ')')) { + goto _calc; + } + + if (!(op2 = get_op (pp)) || (look_ahead = get_prec (op2->kind)) >= prec) { + + if (op2) { *pp -= strlen (op2->word); } + break; + + } + + caret = (*pp -= strlen (op2->word)); + + if (is_end_of_line[(int) **pp]) { + break; + } + + rhs = eval_expr (rhs, start, pp, prec, brackets); + + if (*pp == caret) { + break; + } + + } + + if (!op2) { break; } + + _calc: + + switch (op->kind) { + + case OP_MUL: + + lhs *= rhs; + break; + + case OP_DIV: + + lhs /= rhs; + break; + + case OP_MOD: + + lhs %= rhs; + break; + + case OP_PLUS: + + lhs += rhs; + break; + + case OP_MINUS: + + lhs -= rhs; + break; + + case OP_LT: + + lhs = (lhs < rhs); + break; + + case OP_GT: + + lhs = (lhs > rhs); + break; + + case OP_LTEQ: + + lhs = (lhs <= rhs); + break; + + case OP_GTEQ: + + lhs = (lhs >= rhs); + break; + + case OP_EQEQ: + + lhs = (lhs == rhs); + break; + + case OP_NOTEQ: + + lhs = (lhs != rhs); + break; + + case OP_AND: + + lhs &= rhs; + break; + + case OP_XOR: + + lhs ^= rhs; + break; + + case OP_OR: + + lhs |= rhs; + break; + + case OP_ANDAND: + + lhs = ((lhs != 0) && (rhs != 0)); + break; + + case OP_OROR: + + lhs = ((lhs != 0) || (rhs != 0)); + break; + + case OP_LSHIFT: + + lhs <<= rhs; + break; + + case OP_RSHIFT: + + lhs >>= rhs; + break; + + default: + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "unimplemented"); + break; + + } + + } + + caret = (*pp = skip_whitespace (*pp)); + + if (!is_end_of_line[(int) **pp]) { + + if (is_name_beginner ((int) **pp) || isdigit ((int) **pp)) { + + while (is_name_part ((int) **pp)) { + (*pp)++; + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing binary operator before token \"%.*s\"", (int) (*pp - caret), caret); + + while (!is_end_of_line[(int) **pp]) { + + if ((brackets && **pp == ')')) { + break; + } + + (*pp)++; + + } + + } else if (op && ((op2 = get_op (pp)) || (brackets && **pp == ')'))) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "operator '%s' has no right operand", op->word); + + if (op2) { + + while (!is_end_of_line[(int) **pp]) { + + if ((brackets && **pp == ')')) { + break; + } + + (*pp)++; + + } + + } + + } else if (**pp == ')') { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "missing '(' in expression"); + + while (!is_end_of_line[(int) **pp]) { + + if ((brackets && **pp == ')')) { + break; + } + + (*pp)++; + + } + + } else { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "token \"%c\" is not valid in preprocessor expressions", **pp); + + while (!is_end_of_line[(int) **pp]) { + + /*if (is_name_beginner ((int) **pp)) { + break; + }*/ + + if (/*isspace ((int) **pp) || */(brackets && **pp == ')')) { + break; + } + + (*pp)++; + + } + + } + + *pp = skip_whitespace (*pp); + return 0; + + } + + return lhs; + +} + +int eval (char *start, char **pp) { + + struct op *op = 0; + uint64_t lhs = 0; + + char *caret = (*pp = skip_whitespace (*pp)); + + if (!is_end_of_line[(int) **pp]) { + + lhs = eval_unary (lhs, start, pp); + + if (*pp == caret && (op = get_op (pp))) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "operator '%s' has no left operand", op->word); + + while (!is_end_of_line[(int) **pp]) { + (*pp)++; + } + + return 0; + + } + + lhs = eval_expr (lhs, start, pp, 15, 0); + + if (!is_end_of_line[(int) **pp]) { + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "#if not empty"); + } + + return (lhs != 0); + + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "#if with no expression"); + return 0; + +} diff --git a/eval.h b/eval.h new file mode 100755 index 0000000..9361938 --- /dev/null +++ b/eval.h @@ -0,0 +1,9 @@ +/****************************************************************************** + * @file eval.h + *****************************************************************************/ +#ifndef _EVAL_H +#define _EVAL_H + +int eval (char *start, char **pp); + +#endif /* _EVAL_H */ diff --git a/expr.c b/expr.c new file mode 100755 index 0000000..a4bd11d --- /dev/null +++ b/expr.c @@ -0,0 +1,800 @@ +/****************************************************************************** + * @file expr.c + *****************************************************************************/ +#include +#include +#include + +#include "expr.h" +#include "int64.h" +#include "lib.h" +#include "parse.h" +#include "report.h" +#include "token.h" + +static int64_s *eval_expr (int64_s *lhs, int outer_prec, int brackets); +static enum token_kind *killat = 0; + +static void cast_integer_constant (int64_s *v, int size, int is_unsigned) { + + unsigned long sign; + + if (!v) { + return; + } + + if (size <= 1) { + + v->low &= 0xffUL; + v->high = 0; + + if (!is_unsigned && (v->low & 0x80UL)) { + + v->low |= 0xffffff00UL; + v->high = 0xffffffffUL; + + } + + return; + + } + + if (size == 2) { + + v->low &= 0xffffUL; + v->high = 0; + + if (!is_unsigned && (v->low & 0x8000UL)) { + v->low |= 0xffff0000UL; + v->high = 0xffffffffUL; + } + + return; + + } + + if (size == 4) { + + sign = v->low & 0x80000000UL; + + v->high = (!is_unsigned && sign) ? 0xffffffffUL : 0; + return; + + } + +} + +static int should_terminate (void) { + + int i; + + if (killat) { + + for (i = 0; ; i++) { + + if (killat[i]) { + + if (tok.kind == killat[i]) { + return 1; + } + + continue; + + } + + break; + + } + + } + + return 0; + +} + +static int64_s *eval_unary (int64_s *lhs) { + + if (token_is_sizeof_keyword ()) { + + int size; + get_token (); + + size = parse_sizeof_value (); + zext64 (lhs, (unsigned long) size); + + return lhs; + + } + + if (tok.kind == TOK_IDENT && resolve_enum_constant (tok.ident, lhs)) { + + get_token (); + return lhs; + + } + + switch (tok.kind) { + + case TOK_CCHAR: case TOK_CINT: case TOK_CUINT: case TOK_CULONG: + case TOK_CLONG: case TOK_CLLONG: case TOK_CULLONG: case TOK_LCHAR: + + lhs->high = tok.val.i.high; + lhs->low = tok.val.i.low; + + get_token (); + return lhs; + + default: + + break; + + } + + if (tok.kind == TOK_XMARK) { + + int64_s tmp = { 0 }, *tmp_p; + + get_token (); + tmp_p = eval_unary (&tmp); + + if (!tmp_p) { + return 0; + } + + tmp = *tmp_p; + zext64 (lhs, is_zero64 (tmp)); + + return lhs; + + } + + if (tok.kind == TOK_TILDE) { + + int flip_bits = 0; + + while (tok.kind == TOK_TILDE) { + + flip_bits = !flip_bits; + get_token (); + + } + + lhs = eval_unary (lhs); + + if (!lhs) { + return 0; + } + + if (flip_bits) { + not64 (lhs); + } + + return lhs; + + } + + if (tok.kind == TOK_MINUS || tok.kind == TOK_PLUS) { + + int sign = 1; + + while (tok.kind == TOK_MINUS || tok.kind == TOK_PLUS) { + + if (tok.kind == TOK_MINUS) { sign = !sign; } + get_token (); + + } + + lhs = eval_unary (lhs); + + if (!lhs) { + return 0; + } + + if (!sign) { + neg64 (lhs); + } + + return lhs; + + } + + if (tok.kind == TOK_AMPER) { + + if (parse_constexpr_address_of_null_member (lhs)) { + return lhs; + } + + return 0; + + } + + if (tok.kind == TOK_LPAREN) { + + char *start = xstrdup (tok.start); + unsigned long caret = tok.caret - tok.start; + + get_token (); + + if (token_starts_type_name ()) { + + int cast_size = 0; + int cast_is_unsigned = 0; + int cast_is_pointer = 0; + + if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) { + + lhs = eval_unary (lhs); + + if (!lhs) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected"); + lhs = 0; + + } else { + + if (cast_is_pointer) { + + cast_size = 4; + cast_is_unsigned = 1; + + } + + cast_integer_constant (lhs, cast_size, cast_is_unsigned); + + } + + free (start); + return lhs; + + } + + } + + lhs = eval_unary (lhs); + lhs = eval_expr (lhs, 14, 1); + + if (tok.kind != TOK_RPAREN) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "missing ')' in expression"); + } else { + get_token (); + } + + free (start); + return lhs; + + } + + lhs->high = 0; + lhs->low = 0; + + return 0; + +} + +#define OP_MUL 0 +#define OP_DIV 1 +#define OP_MOD 2 +#define OP_PLUS 3 +#define OP_MINUS 4 +#define OP_LT 5 +#define OP_GT 6 +#define OP_LTEQ 7 +#define OP_GTEQ 8 +#define OP_EQEQ 9 +#define OP_NOTEQ 11 +#define OP_AND 12 +#define OP_XOR 13 +#define OP_OR 14 +#define OP_ANDAND 15 +#define OP_OROR 16 +#define OP_LSHIFT 17 +#define OP_RSHIFT 18 +#define OP_QUEST 19 +#define OP_MAX 20 + +struct op { + + char *word; + int kind; + +}; + +static struct op *get_op (char *pp) { + + static struct op kws[] = { + + { "<=", OP_LTEQ }, + { ">=", OP_GTEQ }, + { "==", OP_EQEQ }, + { "!=", OP_NOTEQ }, + { "&&", OP_ANDAND }, + { "||", OP_OROR }, + { "<<", OP_LSHIFT }, + { ">>", OP_RSHIFT }, + + { "*", OP_MUL }, + { "/", OP_DIV }, + { "%", OP_MOD }, + { "+", OP_PLUS }, + { "-", OP_MINUS }, + { "<", OP_LT }, + { ">", OP_GT }, + { "&", OP_AND }, + { "^", OP_XOR }, + { "|", OP_OR }, + { "?", OP_QUEST } + + }; + + struct op *kw; + unsigned int i; + + for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) { + + kw = &kws[i]; + + if (strncmp (pp, kw->word, strlen (kw->word)) == 0) { + return kw; + } + + } + + return 0; + +} + +static int get_prec (int kind) { + + switch (kind) { + + case OP_MUL: case OP_DIV: case OP_MOD: + + return 3; + + case OP_PLUS: case OP_MINUS: + + return 4; + + case OP_LSHIFT: case OP_RSHIFT: + + return 5; + + case OP_LT: case OP_GT: case OP_LTEQ: case OP_GTEQ: + + return 6; + + case OP_EQEQ: case OP_NOTEQ: + + return 7; + + case OP_AND: + + return 8; + + case OP_XOR: + + return 9; + + case OP_OR: + + return 10; + + case OP_ANDAND: + + return 11; + + case OP_OROR: + + return 12; + + case OP_QUEST: + + return 13; + + default: + + break; + + } + + return 100; + +} + +static void recover_bad_const_operand (void) { + + int depth = 0; + + if (tok.kind == TOK_EOF || should_terminate ()) { + return; + } + + do { + + if (tok.kind == TOK_LPAREN) { + + depth++; + + get_token (); + continue; + + } + + if (tok.kind == TOK_RPAREN) { + + if (depth > 0) { + + depth--; + + get_token (); + continue; + + } + + if (should_terminate ()) { + + get_token (); + continue; + + } + + break; + + } + + get_token (); + + } while (tok.kind != TOK_EOF && (depth > 0 || !should_terminate ())); + +} + +static int64_s *eval_expr (int64_s *lhs, int outer_prec, int brackets) { + + struct op *op = 0, *op2 = 0; + + int64_s rhs = { 0 }, *tmp; + int prec, look_ahead; + + unsigned long index, lineno; + + for (;;) { + + if (brackets && tok.kind == TOK_RPAREN) { + return lhs; + } + + if (tok.kind == TOK_EOF || should_terminate ()) { + break; + } + + if (!(op = get_op (tok.ident)) || (prec = get_prec (op->kind)) >= outer_prec) { + + if (outer_prec == 15) { + break; + } + + return lhs; + + } + + get_token (); + + if (tok.kind == TOK_EOF || should_terminate ()) { + break; + } + + rhs.high = 0; + rhs.low = 0; + + if (op->kind == OP_QUEST) { + + int64_s left = { 0 }, right = { 0 }, *tmp; + + if (!(tmp = eval_unary (&left))) { + break; + } + + left = *eval_expr (tmp, 14, brackets); + + assert (tok.kind == TOK_COLON); + get_token (); + + if (!(tmp = eval_unary (&right))) { + break; + } + + right = *eval_expr (&right, 14, brackets); + + if (!is_zero64 (*lhs)) { + + lhs->high = left.high; + lhs->low = left.low; + + } else { + + lhs->high = right.high; + lhs->low = right.low; + + } + + continue; + + } + + if (!(tmp = eval_unary (&rhs))) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected"); + + recover_bad_const_operand (); + break; + + } + + for (;;) { + + rhs.high = tmp->high; + rhs.low = tmp->low; + + if (tok.kind == TOK_EOF || should_terminate () || (brackets && tok.kind == TOK_RPAREN)) { + goto _calc; + } + + if (!(op2 = get_op (tok.ident)) || (look_ahead = get_prec (op2->kind)) >= prec) { + break; + } + + /* Get the current index and line number. */ + index = tok.caret - tok.start; + lineno = get_line_number (); + + /* Try evaluation. */ + tmp = eval_expr (&rhs, prec, brackets); + + /* If the index and line number are the same them we;re done. */ + if (index == (unsigned long) (tok.caret - tok.start) && lineno == get_line_number ()) { + break; + } + + } + + if (!op2) { break; } + + _calc: + + switch (op->kind) { + + case OP_MUL: + + mul64 (lhs, rhs); + break; + + case OP_DIV: + + div64 (lhs, rhs); + break; + + case OP_MOD: + + mod64 (lhs, rhs); + break; + + case OP_PLUS: + + add64 (lhs, rhs); + break; + + case OP_MINUS: + + sub64 (lhs, rhs); + break; + + case OP_LT: + + zext64 (lhs, lt64_unsigned (*lhs, rhs)); + break; + + case OP_GT: + + zext64 (lhs, gt64_unsigned (*lhs, rhs)); + break; + + case OP_LTEQ: + + zext64 (lhs, lte64_unsigned (*lhs, rhs)); + break; + + case OP_GTEQ: + + zext64 (lhs, gte64_unsigned (*lhs, rhs)); + break; + + case OP_EQEQ: + + zext64 (lhs, eq64 (*lhs, rhs)); + break; + + case OP_NOTEQ: + + zext64 (lhs, !eq64 (*lhs, rhs)); + break; + + case OP_AND: + + and64 (lhs, rhs); + break; + + case OP_XOR: + + xor64 (lhs, rhs); + break; + + case OP_OR: + + or64 (lhs, rhs); + break; + + case OP_ANDAND: + + zext64 (lhs, !is_zero64 (*lhs) && !is_zero64 (rhs)); + break; + + case OP_OROR: + + zext64 (lhs, !is_zero64 (*lhs) || !is_zero64 (rhs)); + break; + + case OP_LSHIFT: + + shl64 (lhs, rhs.low); + break; + + case OP_RSHIFT: + + shr64 (lhs, rhs.low); + break; + + default: + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "unimplemented"); + break; + + } + + } + + if (!(tok.kind == TOK_EOF || should_terminate ())) { + + if (tok.kind >= TOK_LCHAR && tok.kind <= TOK_CULONG) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing binary operator before token \"%s\"", tok.ident); + + while (!(tok.kind == TOK_EOF || should_terminate ())) { + + if (brackets && tok.kind == TOK_RPAREN) { + break; + } + + get_token (); + + } + + } else if (op && ((op2 = get_op (tok.ident)) || (brackets && tok.kind == TOK_RPAREN))) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "operator '%s' has no right operand", op->word); + + if (op2) { + + get_token (); + + while (!(tok.kind == TOK_EOF || should_terminate ())) { + + if (brackets && tok.kind == TOK_RPAREN) { + break; + } + + get_token (); + + } + + } + + } else if (brackets && tok.kind == TOK_RPAREN) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing '(' in expression"); + + while (!(tok.kind == TOK_EOF || should_terminate ())) { + + if (brackets && tok.kind == TOK_RPAREN) { + break; + } + + get_token (); + + } + + } else { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected"); + + while (!(tok.kind == TOK_EOF || should_terminate ())) { + + if (brackets && tok.kind == TOK_RPAREN) { + break; + } + + get_token (); + + } + + } + + lhs->high = 0; + lhs->low = 0; + + } + + return lhs; + +} + +int expr_const (enum token_kind *k) { + + char *start = xstrdup (tok.start); + unsigned long caret = tok.caret - tok.start; + + int64_s wc = expr_const64 (k); + unsigned long sign = wc.low & 0x80000000UL; + + int c = (int) wc.low; + + if (!((wc.high == 0) || (sign && wc.high == 0xFFFFFFFFUL))) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "constant exceeds 32 bit"); + } + + free (start); + return c; + +} + +int64_s expr_const64 (enum token_kind *k) { + + struct op *op = 0; + int64_s lhs = { 0 }, *tmp; + + char *start = xstrdup (tok.start); + unsigned long caret = tok.caret - tok.start; + + killat = k; + + if (!(tmp = eval_unary (&lhs))) { + + if ((op = get_op (tok.ident))) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, start + caret, "operator '%s' has no left operand", op->word); + + while (!(tok.kind == TOK_EOF || should_terminate ())) { + get_token (); + } + + free (start); + + lhs.high = 0; + lhs.low = 0; + + return lhs; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "integer constant expression expected"); + + recover_bad_const_operand (); + tmp = &lhs; + + } + + lhs = *eval_expr (tmp, 15, 0); + + free (start); + return lhs; + +} diff --git a/expr.h b/expr.h new file mode 100755 index 0000000..8cdf350 --- /dev/null +++ b/expr.h @@ -0,0 +1,13 @@ +/****************************************************************************** + * @file expr.h + *****************************************************************************/ +#ifndef _EXPR_H +#define _EXPR_H + +#include "int64.h" +#include "token.h" + +int expr_const (enum token_kind *k); +int64_s expr_const64 (enum token_kind *k); + +#endif /* _EXPR_H */ diff --git a/hashtab.c b/hashtab.c new file mode 100755 index 0000000..3f26b37 --- /dev/null +++ b/hashtab.c @@ -0,0 +1,217 @@ +/****************************************************************************** + * @file hashtab.c + *****************************************************************************/ +#include +#include +#include + +#include "hashtab.h" + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key); + +static int adjust_capacity (struct hashtab *table, unsigned long new_capacity) { + + struct hashtab_entry *new_entries, *old_entries; + unsigned long i, new_count, old_capacity; + + if ((new_entries = malloc (sizeof (*new_entries) * new_capacity)) == NULL) { + return -2; + } + + for (i = 0; i < new_capacity; i++) { + + struct hashtab_entry *entry = &new_entries[i]; + + entry->key = NULL; + entry->value = NULL; + + } + + old_entries = table->entries; + old_capacity = table->capacity; + + new_count = 0; + + for (i = 0; i < old_capacity; i++) { + + struct hashtab_entry *entry = &old_entries[i], *dest; + + if (entry->key == NULL) { + continue; + } + + dest = find_entry (new_entries, new_capacity, entry->key); + + dest->key = entry->key; + dest->value = entry->value; + + new_count++; + + } + + free (old_entries); + + table->capacity = new_capacity; + table->count = new_count; + table->entries = new_entries; + table->used = new_count; + + return 0; + +} + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key) { + + struct hashtab_entry *tombstone = NULL; + unsigned long index; + + for (index = key->hash % capacity; ; index = (index + 1) % capacity) { + + struct hashtab_entry *entry = &entries[index]; + + if (entry->key == NULL) { + + if (entry->value == NULL) { + + if (tombstone == NULL) { + return entry; + } + + return tombstone; + + } else if (tombstone == NULL) { + tombstone = entry; + } + + } else if (entry->key->bytes == key->bytes) { + + if (memcmp (entry->key->chars, key->chars, key->bytes) == 0 && entry->key->hash == key->hash) { + return entry; + } + + } + + } + +} + +static unsigned long hash_string (const void *p, unsigned long length) { + + unsigned char *str = (unsigned char *) p; + unsigned long i; + + unsigned long result = 0; + + for (i = 0; i < length; i++) { + result = (((unsigned short) str[i]) << 4) + (result >> 9) + result + (result >> 3) + (((unsigned short) str[i]) << 2) - (result << 12); + } + + return result; + +} + +struct hashtab_name *hashtab_alloc_name (const char *str) { + + unsigned long bytes = strlen (str), hash = hash_string (str, bytes); + struct hashtab_name *name; + + if (!(name = malloc (sizeof (*name)))) { + return 0; + } + + name->bytes = bytes; + name->chars = str; + name->hash = hash; + + return name; + +} + +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name) { + + struct hashtab_name *key; + struct hashtab_entry *entry; + + if (!table || table->count == 0 || !(key = hashtab_alloc_name (name))) { + return 0; + } + + entry = find_entry (table->entries, table->capacity, key); + free (key); + + return entry->key; + +} + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if (!table || table->count == 0) { + return 0; + } + + entry = find_entry (table->entries, table->capacity, key); + + if (!entry->key) { + return 0; + } + + return entry->value; + +} + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value) { + + const int MIN_CAPACITY = 15; + + struct hashtab_entry *entry; + int ret = 0; + + if (table->used >= table->capacity / 2) { + + long capacity = table->capacity * 2 - 1; + + if (capacity < MIN_CAPACITY) { + capacity = MIN_CAPACITY; + } + + if ((ret = adjust_capacity (table, capacity))) { + return ret; + } + + } + + entry = find_entry (table->entries, table->capacity, key); + + if (!entry->key) { + + table->count++; + + if (!entry->value) { + table->used++; + } + + } + + entry->key = key; + entry->value = value; + + return 0; + +} + +void hashtab_remove (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if ((entry = find_entry (table->entries, table->capacity, key)) != NULL) { + + entry->key = 0; + entry->value = 0; + + --table->count; + + } + +} diff --git a/hashtab.h b/hashtab.h new file mode 100755 index 0000000..5ab0492 --- /dev/null +++ b/hashtab.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * @file hashtab.h + *****************************************************************************/ +#ifndef _HASHTAB_H +#define _HASHTAB_H + +struct hashtab_name { + + const char *chars; + unsigned long bytes, hash; + +}; + +struct hashtab_entry { + + struct hashtab_name *key; + void *value; + +}; + +struct hashtab { + + struct hashtab_entry *entries; + int capacity, count, used; + +}; + +struct hashtab_name *hashtab_alloc_name (const char *str); +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name); + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key); + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value); +void hashtab_remove (struct hashtab *table, struct hashtab_name *key); + +#endif /* _HASHTAB_H */ diff --git a/int64.c b/int64.c new file mode 100644 index 0000000..8ad59f5 --- /dev/null +++ b/int64.c @@ -0,0 +1,713 @@ +/****************************************************************************** + * @file int64.c + *****************************************************************************/ +#include +#include + +#include "int64.h" +#include "report.h" + +extern const char *get_filename (void); +extern unsigned long get_line_number (void); + +void add64 (int64_s *a, int64_s b) { + + unsigned long raw_sum, carry; + + /* 1. Calculate the raw result */ + raw_sum = a->low + b.low; + + /* 2. Manually force 32-bit wrap around using a mask */ + /* This ensures that any "1" that would be in the 33rd bit is stripped */ + a->low = raw_sum & 0xFFFFFFFFUL; + + /* 3. Extract the carry: if raw_sum > 0xFFFFFFFF, we had a carry */ + carry = (raw_sum > 0xFFFFFFFFUL) ? 1 : 0; + + /* 4. Update the high part */ + a->high = (a->high + b.high + carry) & 0xFFFFFFFFUL; + +} + +void mul64 (int64_s *a, int64_s b) { + + /* Extract 16-bit halves of the low words */ + unsigned long al = a->low & 0xFFFF; + unsigned long ah = (a->low >> 16) & 0xFFFF; + unsigned long bl = b.low & 0xFFFF; + unsigned long bh = (b.low >> 16) & 0xFFFF; + + /* 1. Calculate the 32x32 -> 64 bit product of (a->low * b.low) */ + unsigned long p0 = al * bl; + unsigned long p1 = al * bh; + unsigned long p2 = ah * bl; + unsigned long p3 = ah * bh; + + /* Handle middle-word carries */ + unsigned long mid = (p0 >> 16) + (p1 & 0xFFFF) + (p2 & 0xFFFF); + + /* Store the new low 32 bits */ + unsigned long new_low = ((mid << 16) | (p0 & 0xFFFF)); + + /* 2. Calculate the high word */ + /* Start with the carries/high-bits from the 32x32 multiply */ + unsigned long new_high = p3 + (p1 >> 16) + (p2 >> 16) + (mid >> 16); + + /* 3. Add the "Cross Multiplication" of the original high word */ + /* Mathematically: (a->high * b.low) + (b.high * a->low) */ + /* Note: We ignore (a->high * b.high) as it overflows 64 bits */ + new_high += (a->high * b.low); + new_high += (b.high * a->low); + + /* Final Assignment */ + a->low = new_low; + a->high = new_high; + +} + +void shl64 (int64_s *a, int n) { + + /* Handle shifts >= 32 separately */ + if (n >= 32) { + + a->high = (a->low << (n - 32)) & 0xFFFFFFFFUL; + a->low = 0; + + } else if (n > 0) { + + /* Bits pushed out of low go into high */ + a->high = ((a->high << n) | (a->low >> (32 - n))) & 0xFFFFFFFFUL; + a->low = (a->low << n) & 0xFFFFFFFFUL; + + } + +} + +void shr64 (int64_s *a, int n) { + + /* Handle shifts >= 32 separately */ + if (n >= 32) { + + a->low = (a->high >> (n - 32)); + a->high = 0; + + } else if (n > 0) { + + /* Bits pushed out of high go into low */ + a->low = ((a->low >> n) | (a->high << (32 - n))) & 0xFFFFFFFFUL; + a->high = (a->high >> n) & 0xFFFFFFFFUL; + + } + +} + +void sub64 (int64_s *a, int64_s b) { + + unsigned long old_low = a->low, borrow; + + /* 1. Perform subtraction and force 32-bit wrapping */ + a->low = (a->low - b.low) & 0xFFFFFFFFUL; + + /* 2. Determine if a borrow occurred: + * In unsigned arithmetic, if (a->low > old_low), we wrapped. + * Because we masked the result, this logic is now 100% stable. + */ + borrow = (old_low < b.low) ? 1 : 0; + + /* 3. Subtract high parts and the borrow, forcing 32-bit wrap */ + /* If high - b.high - borrow is negative, the & 0xFFFFFFFFUL + will wrap it correctly to the 32-bit representation */ + a->high = (a->high - b.high - borrow) & 0xFFFFFFFFUL; + +} + +void and64 (int64_s *a, int64_s b) { + + a->low &= b.low; + a->high &= b.high; + +} + +void or64 (int64_s *a, int64_s b) { + + a->low |= b.low; + a->high |= b.high; + +} + +void xor64 (int64_s *a, int64_s b) { + + a->low ^= b.low; + a->high ^= b.high; + +} + +/* Returns 1 if a > b, -1 if a < b, and 0 if a == b */ +int compare64 (int64_s a, int64_s b) { + + /* Compare the high bits first (the most significant part) */ + if (a.high > b.high) return 1; + if (a.high < b.high) return -1; + + /* If high bits are equal, compare the low bits */ + if (a.low > b.low) return 1; + if (a.low < b.low) return -1; + + /* If both are identical */ + return 0; + +} + +void div64 (int64_s *a, int64_s b) { + + int64_s quotient = { 0, 0 }; + int64_s remainder = { 0, 0 }; + + int i; + + /* If divisor is zero, you should handle this as a fatal compiler error */ + if (b.low == 0 && b.high == 0) { + return; + } + + /* Iterate through all 64 bits of the dividend */ + for (i = 63; i >= 0; i--) { + + /* 1. Shift the working remainder left to make room for the next bit */ + shl64 (&remainder, 1); + + /* 2. Bring in the i-th bit from the dividend (a) */ + /* Use a mask to check the bit and pull it into remainder.low */ + if (i >= 32) { + + if ((a->high >> (i - 32)) & 1UL) { + remainder.low |= 1; + } + + } else { + + if ((a->low >> i) & 1UL) { + remainder.low |= 1; + } + + } + + /* 3. If remainder >= divisor, we can subtract and set quotient bit */ + if (compare64 (remainder, b) >= 0) { + + sub64 (&remainder, b); + + /* Set the i-th bit in the quotient */ + if (i >= 32) { + quotient.high |= (1UL << (i - 32)); + } else { + quotient.low |= (1UL << i); + } + + } + + } + + /* Overwrite dividend with the resulting quotient */ + a->low = quotient.low; + a->high = quotient.high; + +} + +void mod64 (int64_s *a, int64_s b) { + + int64_s remainder = {0, 0}; + int i; + + /* 1. Safety check for division by zero */ + if (b.low == 0 && b.high == 0) { + return; + } + + /* 2. Iterate through all 64 bits */ + for (i = 63; i >= 0; i--) { + + /* Shift remainder left by 1 */ + shl64 (&remainder, 1); + + /* Bring in the i-th bit from 'a' */ + if (i >= 32) { + + if ((a->high >> (i - 32)) & 1UL) { + remainder.low |= 1; + } + + } else { + + if ((a->low >> i) & 1UL) { + remainder.low |= 1; + } + + } + + /* 3. Compare and Subtract (The core logic) */ + if (compare64 (remainder, b) >= 0) { + sub64 (&remainder, b); + } + + } + + /* 4. Update 'a' with the final remainder */ + a->low = remainder.low; + a->high = remainder.high; + +} + +void zext64 (int64_s *dest, unsigned long input) { + + dest->low = input; + dest->high = 0; /* Clear the upper bits */ + +} + +void sext64 (int64_s *dest, long input) { + + dest->low = (unsigned long)input; + + /* If the 31st bit (MSB) is 1, the number is negative */ + if (input & 0x80000000UL) { + dest->high = 0xFFFFFFFFUL; + } else { + dest->high = 0x00000000UL; + } + +} + +void mask64 (int64_s *a, int bits) { + + unsigned long low_mask; + unsigned long high_mask; + unsigned long new_low, new_high; + + if (bits >= 64) { + return; + } + + if (bits <= 0) { + + if (a->high != 0 || a->low != 0) { + report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x00000000", a->high, a->low); + } + + a->high = 0; + a->low = 0; + + return; + + } + + if (bits < 32) { + + low_mask = (1UL << bits) - 1; + + new_low = a->low & low_mask; + new_high = 0; + + if (a->high != 0 || (a->low & ~low_mask) != 0) { + report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX", a->high, a->low, new_low); + } + + + } else if (bits == 32) { + + new_low = a->low; + new_high = 0; + + if (a->high != 0) { + report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX", a->high, a->low, new_low); + } + + } else { /* 33..63 */ + + high_mask = (1UL << (bits - 32)) - 1; + + new_low = a->low; + new_high = a->high & high_mask; + + if ((a->high & ~high_mask) != 0) { + report_at (get_filename(), get_line_number(), REPORT_WARNING, "implicit truncation of 0x%08lX%08lX to 0x%08lX%08lX", a->high, a->low, new_high, new_low); + } + + } + + a->high = new_high; + a->low = new_low; + + report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "???"); + +} + +/* The specialized wrapper for standard 32-bit int truncation */ +void trunc64 (int64_s *a) { + mask64 (a, 32); +} + +void inc64 (int64_s *a) { + + a->low++; + + if (a->low == 0) { /* Carry occurred */ + a->high++; + } + +} + +void dec64 (int64_s *a) { + + if (a->low == 0) { /* Borrow needed */ + a->high--; + } + + a->low--; + +} + +void not64 (int64_s *a) { + + /* Flip bits and then force them back into 32-bit bounds */ + a->low = (~a->low) & 0xFFFFFFFFUL; + a->high = (~a->high) & 0xFFFFFFFFUL; + +} + +void neg64 (int64_s *a) { + + not64 (a); + inc64 (a); + +} + +int is_zero64 (int64_s a) { + return (a.low == 0 && a.high == 0); +} + +int eq64 (int64_s a, int64_s b) { + return (a.low == b.low && a.high == b.high); +} + +int neq64 (int64_s a, int64_s b) { + return !eq64 (a, b); +} + +int lt64_unsigned (int64_s a, int64_s b) { + + if (a.high != b.high) { + return a.high < b.high; + } + + return a.low < b.low; + +} + +int lte64_unsigned (int64_s a, int64_s b) { + + if (a.high != b.high) { + return a.high < b.high; + } + + return a.low <= b.low; + +} + +int lt64_signed (int64_s a, int64_s b) { + + unsigned long a_sign = (a.high >> 31) & 1; + unsigned long b_sign = (b.high >> 31) & 1; + + /* If signs differ, negative is less than positive */ + if (a_sign != b_sign) { + return a_sign > b_sign; + } + + /* If signs are the same, treat as unsigned */ + return lt64_unsigned (a, b); + +} + +/* Less than or equal to (Signed) */ +int lte64_signed (int64_s a, int64_s b) { + + /* A <= B is true if A < B or A == B */ + return lt64_signed (a, b) || eq64 (a, b); + +} + +/* Greater than or equal to (unsigned) */ +int gte64_unsigned (int64_s a, int64_s b) { + + /* If it's NOT less than, it is greater than or equal to */ + return !lt64_unsigned (a, b); + +} + +/* Greater than or equal to (signed) */ +int gte64_signed (int64_s a, int64_s b) { + + /* If it's NOT less than, it is greater than or equal to */ + return !lt64_signed (a, b); + +} + +/* Greater than (Unsigned) */ +int gt64_unsigned (int64_s a, int64_s b) { + + /* If it's not less than or equal to, it must be greater than */ + return !lte64_unsigned (a, b); + +} + +/* Greater than (Signed) */ +int gt64_signed (int64_s a, int64_s b) { + + /* If it's not less than or equal to, it must be greater than */ + return !lte64_signed (a, b); + +} + +void ld_to_u64 (int64_s *out, long double x) { + + long double base = 4294967296.0L; /* 2^32 */ + long double hi, lo; + + if (x <= 0.0L) { + + out->low = 0; + out->high = 0; + + return; + + } + + /* optional clamp */ + if (x >= 18446744073709551615.0L) { /* 2^64 - 1 */ + + out->low = 0xFFFFFFFFUL; + out->high = 0xFFFFFFFFUL; + + return; + + } + + hi = x / base; + out->high = (unsigned long) hi; + + lo = x - ((long double) out->high * base); + out->low = (unsigned long) lo; + +} + +void ld_to_i64 (int64_s *out, long double x) { + + long double limit = 9223372036854775808.0L; /* 2^63 */ + + if (x >= limit) { + + /* INT64_MAX */ + out->low = 0xFFFFFFFFUL; + out->high = 0x7FFFFFFFUL; + + return; + + } + + if (x < -limit) { + + /* INT64_MIN */ + out->low = 0x00000000UL; + out->high = 0x80000000UL; + + return; + + } + + if (x >= 0.0L) { + + ld_to_u64 (out, x); + return; + + } + + /* negative case */ + { + + int64_s tmp; + ld_to_u64 (&tmp, -x); + + *out = tmp; + neg64 (out); + + } + +} + +void sign_extend_masked (int64_s *v, unsigned long mask) { + + unsigned long sign; + + mask &= 0xFFFFFFFFUL; + sign = ((mask >> 1) + 1) & 0xFFFFFFFFUL; + + v->low &= 0xFFFFFFFFUL; + + if (v->low & sign) { + + v->low = (v->low | (~mask)) & 0xFFFFFFFFUL; + v->high = 0xFFFFFFFFUL; + + } else { + + v->low &= mask; + v->high = 0; + + } + + v->low &= 0xFFFFFFFFUL; + v->high &= 0xFFFFFFFFUL; + +} + +static void norm64 (int64_s *v) { + + v->low &= U32_MASK; + v->high &= U32_MASK; + +} + +long double u64_to_ld (int64_s v) { + + norm64 (&v); + return (long double) v.high * 4294967296.0L + (long double) v.low; + +} + +long double i64_to_ld (int64_s v) { + + norm64 (&v); + + if (!(v.high & 0x80000000UL)) { + return (long double) v.high * 4294967296.0L + (long double) v.low; + } + + neg64 (&v); + return -((long double) v.high * 4294967296.0L + (long double) v.low); + +} + +int int64_is_negative (int64_s v) { + return (v.high & 0x80000000LU) != 0; +} + +int int64_fits_uint (int64_s v) { + + /* assumes unsigned int is 32-bit */ + return v.high == 0; + +} + +int int64_fits_int (int64_s v) { + + /* range: -2147483648 .. 2147483647 */ + if (v.high == 0) { + return v.low <= 0x7fffffffLU; + } + + if ((long) v.high == -1) { + return v.low >= 0x80000000LU; + } + + return 0; + +} + +void parse_string_to_i64 (int64_s *val, const char *str) { + + unsigned long l_low, l_high; + unsigned long res_low, res_mid; + unsigned long carry1, carry2; + + unsigned long old_low; + int base = 10, digit; + + char ch; + + val->low = 0; + val->high = 0; + + /* 1. Skip leading whitespace */ + while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r') { + str++; + } + + /* 2. Detect Base */ + if (*str == '0') { + + base = 8; + str++; + + if (*str == 'x' || *str == 'X') { + + base = 16; + str++; + + } + + } + + /* 3. Process Digits */ + for (;;) { + + ch = *str; + + /* Determine digit value */ + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else if (base == 16 && ch >= 'a' && ch <= 'f') { + digit = ch - 'a' + 10; + } else if (base == 16 && ch >= 'A' && ch <= 'F') { + digit = ch - 'A' + 10; + } else { + /* Not a valid digit for the current base */ + break; + } + + if (digit >= base) { + break; + } + + /* --- Math: val = (val * base) + digit --- */ + + /* Multiply existing val->low by base */ + l_low = val->low & 0xFFFF; + l_high = (val->low >> 16) & 0xFFFF; + + res_low = l_low * base; + carry1 = res_low >> 16; + + res_mid = (l_high * base) + carry1; + carry2 = res_mid >> 16; + + /* Update high part: (val->high * base) + carry from low part */ + val->high = (val->high * base) + carry2; + + /* Finalize low part */ + val->low = ((res_mid & 0xFFFF) << 16) | (res_low & 0xFFFF); + + /* Add the new digit */ + old_low = val->low; + val->low += (unsigned long) digit; + + /* Handle carry from addition into the high word */ + if (val->low < old_low) { + val->high++; + } + + str++; + + } + +} diff --git a/int64.h b/int64.h new file mode 100644 index 0000000..c3529f2 --- /dev/null +++ b/int64.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * @file int64.h + *****************************************************************************/ +#ifndef _INT64_H +#define _INT64_H + +typedef struct { + + unsigned long low; + unsigned long high; + +} int64_s; + +void add64 (int64_s *a, int64_s b); +void div64 (int64_s *a, int64_s b); +void mod64 (int64_s *a, int64_s b); +void mul64 (int64_s *a, int64_s b); +void shl64 (int64_s *a, int n); +void shr64 (int64_s *a, int n); +void sub64 (int64_s *a, int64_s b); + +void and64 (int64_s *a, int64_s b); +void or64 (int64_s *a, int64_s b); +void xor64 (int64_s *a, int64_s b); + +void mask64 (int64_s *a, int bits); +void trunc64 (int64_s *a); + +void inc64 (int64_s *a); +void dec64 (int64_s *a); + +void not64 (int64_s *a); +void neg64 (int64_s *a); + +void zext64 (int64_s *dest, unsigned long input); +void sext64 (int64_s *dest, long input); + +int compare64 (int64_s a, int64_s b); +int is_zero64 (int64_s a); + +int eq64 (int64_s a, int64_s b); +int neq64 (int64_s a, int64_s b); + +int lt64_unsigned (int64_s a, int64_s b); +int lte64_unsigned (int64_s a, int64_s b); + +int lt64_signed (int64_s a, int64_s b); +int lte64_signed (int64_s a, int64_s b); + +int gte64_unsigned (int64_s a, int64_s b); +int gte64_signed (int64_s a, int64_s b); + +int gt64_unsigned (int64_s a, int64_s b); +int gt64_signed (int64_s a, int64_s b); + +void ld_to_u64 (int64_s *out, long double x); +void ld_to_i64 (int64_s *out, long double x); + +int int64_is_negative (int64_s v); +int int64_fits_uint (int64_s v); +int int64_fits_int (int64_s v); + +void sign_extend_masked (int64_s *v, unsigned long mask); +void parse_string_to_i64 (int64_s *val, const char *str); + +#define U32_MASK ((((unsigned long) 0xFFFFU) << 16) | 0xFFFFU) + +long double u64_to_ld (int64_s v); +long double i64_to_ld (int64_s v); + +#endif /* _INT64_H */ \ No newline at end of file diff --git a/lex.c b/lex.c new file mode 100755 index 0000000..c68a5aa --- /dev/null +++ b/lex.c @@ -0,0 +1,35 @@ +/****************************************************************************** + * @file lex.c + *****************************************************************************/ +#include "lex.h" + +char is_end_of_line[256] = { 0 }; +char lex_table[256] = { 0 }; + +void lex_init (void) { + + int i; + + is_end_of_line[0] = 1; + is_end_of_line[10] = 1; + + /*lex_table[36] = LEX_NAME_START | LEX_NAME_PART;*/ + /*lex_table[46] = LEX_NAME_START | LEX_NAME_PART;*/ + + for (i = 48; i < 58; i++) { + lex_table[i] = LEX_NAME_PART; + } + + /*lex_table[63] = LEX_NAME_START | LEX_NAME_PART;*/ + + for (i = 65; i < 91; i++) { + lex_table[i] = LEX_NAME_START | LEX_NAME_PART; + } + + lex_table[95] = LEX_NAME_START | LEX_NAME_PART; + + for (i = 97; i < 123; i++) { + lex_table[i] = LEX_NAME_START | LEX_NAME_PART; + } + +} diff --git a/lex.h b/lex.h new file mode 100755 index 0000000..f295e8b --- /dev/null +++ b/lex.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * @file lex.h + *****************************************************************************/ +#ifndef _LEX_H +#define _LEX_H + +#define LEX_NAME_PART 0x0001 +#define LEX_NAME_START 0x0002 + +extern char is_end_of_line[]; +extern char lex_table[]; + +#define is_name_beginner(c) (lex_table[(c)] & LEX_NAME_START) +#define is_name_part(c) (lex_table[(c)] & LEX_NAME_PART) + +void lex_init (void); + +#endif /* _LEX_H */ diff --git a/lib.c b/lib.c new file mode 100755 index 0000000..71506f1 --- /dev/null +++ b/lib.c @@ -0,0 +1,781 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "cc.h" +#include "lex.h" +#include "lib.h" +#include "report.h" +#include "vector.h" + +#if defined (_WIN32) +# define PATHSEP ';' +#else +# define PATHSEP ':' +#endif + +struct cc_option { + + const char *name; + int idx, flags; + +}; + +#define CC_OPTION_NO_ARG 0 +#define CC_OPTION_HAS_ARG 1 +#define CC_OPTION_EQUALS_ARG 3 + +#define CC_OPTION_NONE 0 +#define CC_OPTION_COMPILE 1 +#define CC_OPTION_DEFINE 2 +#define CC_OPTION_HELP 3 +#define CC_OPTION_INCLUDE 4 +#define CC_OPTION_NO_LEADING_UNDERSCORE 5 +#define CC_OPTION_NO_LINEMARKERS 6 +#define CC_OPTION_MASM 7 +#define CC_OPTION_MLONG64 8 +#define CC_OPTION_OUTFILE 9 +#define CC_OPTION_PEDANTIC 10 +#define CC_OPTION_PREPOCESS 11 +#define CC_OPTION_STD 12 +#define CC_OPTION_TRANDITIONAL_LINEMARKERS 13 +#define CC_OPTION_UNDEF 14 + +static struct cc_option opts[] = { + + { "-pedantic", CC_OPTION_PEDANTIC, CC_OPTION_NO_ARG }, + { "-std", CC_OPTION_STD, CC_OPTION_EQUALS_ARG }, + + { "-D", CC_OPTION_DEFINE, CC_OPTION_HAS_ARG }, + { "-I", CC_OPTION_INCLUDE, CC_OPTION_HAS_ARG }, + { "-U", CC_OPTION_UNDEF, CC_OPTION_HAS_ARG }, + + { "-P", CC_OPTION_NO_LINEMARKERS, CC_OPTION_NO_ARG }, + { "-o", CC_OPTION_OUTFILE, CC_OPTION_HAS_ARG }, + + { "-E", CC_OPTION_PREPOCESS, CC_OPTION_NO_ARG }, + { "-S", CC_OPTION_COMPILE, CC_OPTION_NO_ARG }, + + { "-fno-leading-underscore", CC_OPTION_NO_LEADING_UNDERSCORE, CC_OPTION_NO_ARG }, + { "-mlong64", CC_OPTION_MLONG64, CC_OPTION_NO_ARG }, + { "-masm", CC_OPTION_MASM, CC_OPTION_EQUALS_ARG }, + + { "--traditional-linemarker-format", CC_OPTION_TRANDITIONAL_LINEMARKERS, CC_OPTION_NO_ARG }, + { "--help", CC_OPTION_HELP, CC_OPTION_NO_ARG }, + + { 0, 0, 0 } + +}; + + +static int strstart (const char *val, const char **str) { + + const char *p = *str; + const char *q = val; + + while (*q != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = p; + return 1; + +} + +static void print_usage (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s [options] file...\n\n", program_name); + fprintf (stderr, "Options:\n\n"); + + fprintf (stderr, " -Dname[=value] Define 'name' with value 'value'.\n"); + fprintf (stderr, " -I DIR Add DIR to include search path.\n"); + fprintf (stderr, " -Uname Undefine 'name'.\n"); + + fprintf (stderr, "\n"); + fprintf (stderr, " -E Preprocess only; do not compile.\n"); + fprintf (stderr, " -S Compile only.\n"); + + fprintf (stderr, "\n"); + fprintf (stderr, " -std= Assume that the input sources are for\n"); + fprintf (stderr, " .\n"); + fprintf (stderr, " Currently only C89 and C90 are\n"); + fprintf (stderr, " supported.\n"); + fprintf (stderr, " -mlong64 Convert long to 64-bit.\n"); + fprintf (stderr, " -masm= Output compatible syntax.\n"); + fprintf (stderr, " Defaults to MASM but also supports\n"); + fprintf (stderr, " GNU AT&T, GNU INTEL and NASM.\n"); + fprintf (stderr, " -P Don't print linemarkers.\n"); + fprintf (stderr, " -o OBJFILE Name the object-file output OBJFILE.\n"); + + fprintf (stderr, "\n"); + fprintf (stderr, " --traditional-linemarker-format Use #line instead of short form.\n"); + fprintf (stderr, " --help Print this help information.\n"); + + fprintf (stderr, "\n"); + + } + +} + + +char *skip_whitespace (char *__p) { + + while (*__p == ' ' || *__p == '\t') { + __p++; + } + + return __p; + +} + +char *symname (char **pp) { + + char *p = *pp; + + if (is_name_beginner ((int) **pp)) { + + while (is_name_part ((int) **pp)) { + (*pp)++; + } + + return xstrndup (p, *pp - p); + + } + + return 0; + +} + +char *xstrdup (const char *__p) { + + char *p = xmalloc (strlen (__p) + 1); + + strcpy (p, __p); + return p; + +} + +char *xstrndup (const char *__p, unsigned long __len) { + + char *p = xmalloc (__len + 1); + + strncpy (p, __p, __len); + return p; + +} + +void add_include_path (const char *__p) { + + char *in = xstrdup (__p); + char *temp = in, *p; + + do { + + for (p = temp; *p != '\0' && *p != PATHSEP; p++) { + + if (*p == '\\') { + *p = '/'; + } + + } + + if ((p - temp) > 0) { + + unsigned int len = (p - temp); + char *path; + + if (*(p - 1) != '/') { + + path = xmalloc (2 + (p - temp) + 2); + sprintf (path, "-I%.*s/", len, temp); + + } else { + + path = xmalloc (2 + (p - temp) + 1); + sprintf (path, "-I%.*s", len, temp); + + } + + list_append (&state->pplist, path); + + } + + temp = (p + 1); + + } while (*p != '\0'); + + free (in); + +} + +void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + long nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *(void ***) ptab; + + if ((nb & (nb - 1)) == 0) { + + if (!nb) { + nb_alloc = 1; + } else { + nb_alloc = nb * 2; + } + + pp = xrealloc (pp, nb_alloc * sizeof (void *)); + *(void ***) ptab = pp; + + } + + pp[nb++] = data; + *nb_ptr = nb; + +} + +void parse_args (int argc, char **argv, int optind) { + + struct cc_option *popt; + const char *optarg, *r; + + if (argc <= optind) { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + if (state->ifile) { + + report_at (program_name, 0, REPORT_ERROR, "more than one file passed as input"); + exit (EXIT_FAILURE); + + } + + state->ifile = xstrdup (r); + continue; + + } + + for (popt = opts; ; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + if (!strstart (p1, &r1)) { + continue; + } + + optarg = r1; + + if (popt->flags & CC_OPTION_HAS_ARG) { + + if (!(popt->flags & ~CC_OPTION_HAS_ARG)) { + + if (*r1 == '\0') { + + if (optind >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else { + + if (*r1 != '=') { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg++; + + } + + } else if (*r1 != '\0') { + continue; + } + + break; + + } + + switch (popt->idx) { + + case CC_OPTION_COMPILE: { + + state->mode = CC_MODE_COMPILE; + break; + + } + + case CC_OPTION_DEFINE : { + + char *arg; + + if (!strchr (optarg, '=')) { + + arg = xmalloc (2 + strlen (optarg) + 3); + sprintf (arg, "-D%s=1", optarg); + + } else { + + arg = xmalloc (2 + strlen (optarg) + 1); + sprintf (arg, "-D%s", optarg); + + } + + list_append (&state->pplist, arg); + break; + + } + + case CC_OPTION_HELP: { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + case CC_OPTION_INCLUDE: { + + add_include_path (optarg); + break; + + } + + case CC_OPTION_MASM: { + + if (strcmp (optarg, "intel") == 0) { + + state->syntax = ASM_SYNTAX_INTEL; + break; + + } + + if (strcmp (optarg, "att") == 0) { + + state->syntax = ASM_SYNTAX_ATT; + break; + + } + + if (strcmp (optarg, "nasm") == 0) { + + state->syntax = ASM_SYNTAX_INTEL | ASM_SYNTAX_NASM; + break; + + } + + report_at (program_name, 0, REPORT_ERROR, "unrecognised -masm= option"); + exit (EXIT_FAILURE); + + } + + case CC_OPTION_MLONG64: { + + state->long64 = 1; + break; + + } + + case CC_OPTION_NO_LEADING_UNDERSCORE: { + + state->no_leading_underscore = 1; + break; + + } + + case CC_OPTION_NO_LINEMARKERS: { + + state->no_linemarkers = 1; + break; + + } + + case CC_OPTION_OUTFILE: { + + if (state->ofile) { + + report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); + exit (EXIT_FAILURE); + + } + + state->ofile = xstrdup (optarg); + break; + + } + + case CC_OPTION_PEDANTIC: { + + state->pedantic = 1; + break; + + } + + case CC_OPTION_PREPOCESS: { + + state->mode = CC_MODE_PREPROCESS; + break; + + } + + case CC_OPTION_STD: { + + if (strcmp (optarg, "c89") == 0 || strcmp (optarg, "c90") == 0) { + + state->std = 90; + break; + + } + + report_at (program_name, 0, REPORT_ERROR, "unrecognised -std= version"); + exit (EXIT_FAILURE); + + } + + case CC_OPTION_TRANDITIONAL_LINEMARKERS: { + + state->traditional_linemarkers = 1; + break; + + } + + case CC_OPTION_UNDEF: { + + char *arg = xmalloc (2 + strlen (optarg) + 1); + sprintf (arg, "-U%s", optarg); + + list_append (&state->pplist, arg); + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + + if (!state->syntax) { + state->syntax = ASM_SYNTAX_MASM | ASM_SYNTAX_INTEL; + } + +#if defined (VERSION) + state->version = VERSION; +#endif + +} + +void *xmalloc (unsigned long __size) { + + void *ptr = malloc (__size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory"); + exit (EXIT_FAILURE); + + } + + memset (ptr, 0, __size); + return ptr; + +} + +void *xrealloc (void *__ptr, unsigned long __size) { + + void *ptr = realloc (__ptr, __size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "failed to reallocate memory)"); + exit (EXIT_FAILURE); + + } + + return ptr; + +} + + +static const char *filename = 0; +static unsigned long line_number = 0; + +const char *get_filename (void) { + return filename; +} + +unsigned long get_line_number (void) { + return line_number; +} + +void get_filename_and_line_number (const char **__filename_p, unsigned long *__line_number_p) { + + *__filename_p = filename; + *__line_number_p = line_number; + +} + +void set_filename (const char *__filename) { + filename = __filename; +} + +void set_line_number (unsigned long __line_number) { + line_number = __line_number; +} + +void set_filename_and_line_number (const char *__filename, unsigned long __line_number) { + + filename = __filename; + line_number = __line_number; + +} + + +static struct vector vec_pragmas = { 0 }; + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) +# include +# include + +struct pragma_entry { unsigned long ino; }; + +char *get_current_directory (void) { + + static char cwd[4096] = { 0 }; + memset (cwd, 0, sizeof (cwd)); + + if (getcwd (cwd, sizeof (cwd))) { + return xstrdup (cwd); + } + + return 0; + +} + +int is_pragma_igored (const char *filename) { + + struct stat st_buf; + + struct pragma_entry *entry; + int i; + + if (stat (filename, &st_buf) >= 0) { + + for (i = 0; i < vec_pragmas.length; i++) { + + entry = vec_pragmas.data[i]; + + if (entry->ino == st_buf.st_ino) { + return 1; + } + + } + + entry = xmalloc (sizeof (*entry)); + entry->ino = st_buf.st_ino; + + vec_push (&vec_pragmas, entry); + + } + + return 0; + +} +#elif defined (_WIN32) +# include + +struct pragma_entry { + + DWORD dwVolumeSerialNumber; + + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + +}; + +char *get_current_directory (void) { + + static TCHAR tszBuffer[4096]; + size_t i; + + DWORD dwRet; + + if ((dwRet = GetCurrentDirectory (sizeof (tszBuffer), tszBuffer)) == 0) { + return 0; + } + + for (i = 0; i < strlen (tszBuffer); i++) { + + if (tszBuffer[i] == '\\') { + tszBuffer[i] = '/'; + } + + } + + return xstrdup (tszBuffer); + +} + +int is_pragma_igored (const char *filename) { + + HANDLE handle; + + struct pragma_entry *entry; + int i; + + BY_HANDLE_FILE_INFORMATION file_info; + + if ((handle = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { + return 0; + } + + if (GetFileInformationByHandle (handle, &file_info)) { + + for (i = 0; i < vec_pragmas.length; i++) { + + entry = vec_pragmas.data[i]; + + if (entry->dwVolumeSerialNumber == file_info.dwVolumeSerialNumber) { + + if (entry->nFileSizeHigh == file_info.nFileSizeHigh && entry->nFileSizeLow == file_info.nFileSizeLow) { + + CloseHandle (handle); + return 1; + + } + + } + + } + + entry = xmalloc (sizeof (*entry)); + + entry->dwVolumeSerialNumber = file_info.dwVolumeSerialNumber; + entry->nFileSizeHigh = file_info.nFileSizeHigh; + entry->nFileSizeLow = file_info.nFileSizeLow; + + vec_push (&vec_pragmas, entry); + + } + + CloseHandle (handle); + return 0; + +} +#else +char *get_current_directory (void) { + return xstrdup (""); +} + +int is_pragma_igored (const char *filename) { + + const char *entry; + int i; + + for (i = 0; i < vec_pragmas.length; i++) { + + entry = vec_pragmas.data[i]; + + if (strcmp (filename, entry) == 0) { + return 1; + } + + } + + vec_push (&vec_pragmas, xstrdup (filename)); + return 0; + +} +#endif + + +const char *real_filename = 0; +static unsigned long real_line_number = 0; + +const char *get_real_filename (void) { + return real_filename; +} + +unsigned long get_real_line_number (void) { + return real_line_number; +} + +void set_real_filename (const char *__filename) { + real_filename = __filename; +} + +void set_real_line_number (unsigned long __line_number) { + real_line_number = __line_number; +} + + +int has_include_stack (void) { + return state->vec_hist.length > 0; +} + +void print_include_stack (void) { + + struct hist_entry *entry; + int i; + + printf ("in file included "); + + for (i = state->vec_hist.length - 1; i >= 0; i--) { + + entry = state->vec_hist.data[i]; + printf ("from %s:%lu", entry->filename, entry->line_number); + + if (i > 0) { + + printf (",\n"); + printf (" "); + + } else { + printf ("\n"); + } + + } + +} diff --git a/lib.h b/lib.h new file mode 100755 index 0000000..2dec618 --- /dev/null +++ b/lib.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * @file lib.h + *****************************************************************************/ +#ifndef _LIB_H +#define _LIB_H + +struct hist_entry { + + const char *filename; + unsigned long line_number; + +}; + +char *xstrdup (const char *__p); +char *xstrndup (const char *__p, unsigned long __len); + +void *xmalloc (unsigned long __size); +void *xrealloc (void *__ptr, unsigned long __size); + +char *skip_whitespace (char *__p); +char *symname (char **pp); + +void add_include_path (const char *__p); +void dynarray_add (void *ptab, long *nb_ptr, void *data); +void parse_args (int argc, char **argv, int optind); + + +const char *get_filename (void); +unsigned long get_line_number (void); + +void set_filename (const char *__filename); +void set_line_number (unsigned long __line_number); + +void set_filename_and_line_number (const char *__filename, unsigned long __line_number); + + +const char *get_real_filename (void); +unsigned long get_real_line_number (void); + +void set_real_filename (const char *__filename); +void set_real_line_number (unsigned long __line_number); + + +int has_include_stack (void); +void print_include_stack (void); + + +char *get_current_directory (void); +int is_pragma_igored (const char *filename); + +#endif /* _LIB_H */ diff --git a/list.c b/list.c new file mode 100755 index 0000000..a7e475b --- /dev/null +++ b/list.c @@ -0,0 +1,39 @@ +/****************************************************************************** + * @file list.c + *****************************************************************************/ +#include "lib.h" +#include "list.h" + +void list_append (struct list **list, void *data) { + + struct list *new = xmalloc (sizeof (*new)); + struct list *old = (*list); + + if (old) { + + new->next = old->next; + old->next = new; + + } else { + new->next = new; + } + + new->data = data; + *list = new; + +} + +unsigned int nlist (struct list *list) { + + unsigned int n = 0; + + if (list) { + + struct list *p = list; + do { n++; } while ((p = p->next) != list); + + } + + return n; + +} diff --git a/list.h b/list.h new file mode 100755 index 0000000..4825c64 --- /dev/null +++ b/list.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * @file list.h + *****************************************************************************/ +#ifndef _LIST_H +#define _LIST_H + +struct list { + + struct list *next; + void *data; + +}; + +void list_append (struct list **list, void *data); + +#endif /* _LIST_H */ diff --git a/ll.c b/ll.c new file mode 100755 index 0000000..b01656c --- /dev/null +++ b/ll.c @@ -0,0 +1,388 @@ +/****************************************************************************** + * @file ll.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "cc.h" +#include "ll.h" +#include "report.h" + +struct load_line_data { + + char *line, *real_line; + + unsigned long capacity, read_size; + unsigned long end_of_prev_real_line; + + unsigned long *new_line_number_p; + +}; + +#define CAPACITY_INCREMENT 256 +extern void get_filename_and_line_number (const char **__filename_p, unsigned long *__line_number_p); + +extern void *xmalloc (unsigned int __size); +extern void *xrealloc (void *__ptr, unsigned int __size); + +int load_line (char **line_p, char **line_end_p, char **real_line_p, unsigned long *real_line_len_p, unsigned long *newlines_p, FILE *ifp, void **load_line_internal_data_p) { + + struct load_line_data *ll_data = *load_line_internal_data_p; + unsigned int pos_in_line = 0, pos_in_real_line = 0, newlines = 0; + + int in_escape = 0, in_double_quote = 0, in_single_quote = 0; + int in_block_comment = 0, in_line_comment = 0, possible_start_or_end_of_comment = 0, skipping_spaces = 0; + + if (ll_data->end_of_prev_real_line) { + + memmove (ll_data->real_line, ll_data->real_line + ll_data->end_of_prev_real_line, ll_data->read_size - ll_data->end_of_prev_real_line); + ll_data->read_size -= ll_data->end_of_prev_real_line; + + } + + while (1) { + + if (pos_in_line >= ll_data->capacity || pos_in_real_line >= ll_data->capacity) { + + ll_data->capacity += CAPACITY_INCREMENT; + + ll_data->line = xrealloc (ll_data->line, ll_data->capacity + 2); + ll_data->real_line = xrealloc (ll_data->real_line, ll_data->capacity + 1); + + } + + if (pos_in_real_line >= ll_data->read_size) { + + ll_data->read_size = fread (ll_data->real_line + pos_in_real_line, 1, ll_data->capacity - pos_in_real_line, ifp) + pos_in_real_line; + + if (ferror (ifp)) { + return 4; + } + + ll_data->real_line[ll_data->read_size] = '\0'; + + } + + copying: + + if (in_block_comment) { + + while (pos_in_real_line < ll_data->read_size) { + + if (possible_start_or_end_of_comment && ll_data->real_line[pos_in_real_line] == '/') { + + possible_start_or_end_of_comment = 0; + pos_in_real_line++; + + in_block_comment = 0; + break; + + } + + possible_start_or_end_of_comment = 0; + + if (ll_data->real_line[pos_in_real_line] == '*') { + possible_start_or_end_of_comment = 1; + } + + if (ll_data->real_line[pos_in_real_line] == '\n') { + newlines++; + } + + pos_in_real_line++; + + } + + } + + if (in_line_comment) { + + while (pos_in_real_line < ll_data->read_size) { + + if (ll_data->real_line[pos_in_real_line] == '\n') { + + in_line_comment = 0; + break; + + } + + pos_in_real_line++; + + } + + } + + if (skipping_spaces) { + + while (pos_in_real_line < ll_data->read_size) { + + if (ll_data->real_line[pos_in_real_line] != ' ' && ll_data->real_line[pos_in_real_line] != '\t') { + + skipping_spaces = 0; + break; + + } + + pos_in_real_line++; + + } + + } + + while (pos_in_real_line < ll_data->read_size && pos_in_line < ll_data->capacity) { + + ll_data->line[pos_in_line] = ll_data->real_line[pos_in_real_line++]; + + if (in_double_quote || in_single_quote) { + + if (in_escape) { + in_escape = 0; + } else if (in_double_quote && ll_data->line[pos_in_line] == '"') { + in_double_quote = 0; + } else if (in_single_quote && ll_data->line[pos_in_line] == '\'') { + in_single_quote = 0; + } else if (ll_data->line[pos_in_line] == '\\') { + in_escape = 1; + } + + if (ll_data->line[pos_in_line] == '\n') { + + int pos = pos_in_line; + + if (pos > 0 && ll_data->line[pos - 1] == '\r') { + ll_data->line[--pos] = '\n'; + } + + if (pos > 0) { + + if (ll_data->line[pos - 1] != '\\') { + + ll_data->line[pos + 1] = '\0'; + ll_data->end_of_prev_real_line = pos_in_real_line; + + *line_p = ll_data->line; + *line_end_p = ll_data->line + pos; + + if (real_line_p) { + *real_line_p = ll_data->real_line; + } + + if (real_line_len_p) { + *real_line_len_p = pos_in_real_line; + } + + *newlines_p = newlines; + return 0; + + } else { + + pos_in_line = pos - 1; + + newlines++; + goto copying; + + } + + } + + } + + } else { + + if (possible_start_or_end_of_comment) { + + if (ll_data->line[pos_in_line] == '*') { + + possible_start_or_end_of_comment = 0; + ll_data->line[pos_in_line - 1] = ' '; + + in_block_comment = 1; + goto copying; + + } else if (ll_data->line[pos_in_line] == '/') { + + possible_start_or_end_of_comment = 0; + + if (state->std != 90) { + + ll_data->line[pos_in_line - 1] = ' '; + + in_line_comment = 1; + goto copying; + + } + + } + + } + + possible_start_or_end_of_comment = 0; + + if (ll_data->line[pos_in_line] == ' ' || ll_data->line[pos_in_line] == '\t') { + + /*ll_data->line[pos_in_line++] = ' ';*/ + + /*skipping_spaces = 1; + goto copying;*/ + + if (ll_data->line[pos_in_line] == '\t') { + + int cnt = 4 - (pos_in_line % 4); + int i = 0; + + ll_data->capacity += CAPACITY_INCREMENT; + + ll_data->line = xrealloc (ll_data->line, ll_data->capacity + 2); + ll_data->real_line = xrealloc (ll_data->real_line, ll_data->capacity + 1); + + for (; i < cnt; i++) { + ll_data->line[pos_in_line++] = ' '; + } + + continue; + + } + + } else if (ll_data->line[pos_in_line] == '\n') { + + if (pos_in_line > 0 && ll_data->line[pos_in_line - 1] == '\r') { + ll_data->line[--pos_in_line] = '\n'; + } + + ll_data->line[pos_in_line + 1] = '\0'; + ll_data->end_of_prev_real_line = pos_in_real_line; + + *line_p = ll_data->line; + *line_end_p = ll_data->line + pos_in_line; + + if (real_line_p) { + *real_line_p = ll_data->real_line; + } + + if (real_line_len_p) { + *real_line_len_p = pos_in_real_line; + } + + *newlines_p = newlines; + return 0; + + } else if (ll_data->line[pos_in_line] == '\\') { + + /*ll_data->line[pos_in_line] = ' ';*/ + /*pos_in_line--;*/ + + while (pos_in_real_line < ll_data->read_size) { + + if (ll_data->real_line[pos_in_real_line] == '\r' || ll_data->real_line[pos_in_real_line] == '\n') { + + if (ll_data->real_line[pos_in_real_line] == '\r') { + pos_in_real_line++; + } + + if (ll_data->real_line[pos_in_real_line] == '\n') { + pos_in_real_line++; + } + + break; + + } + + pos_in_real_line++; + + } + + newlines++; + continue; + + } else if (ll_data->line[pos_in_line] == '"') { + in_double_quote = 1; + } else if (ll_data->line[pos_in_line] == '\'') { + in_single_quote = 1; + } else if (ll_data->line[pos_in_line] == '/') { + possible_start_or_end_of_comment = 1; + } + + } + + pos_in_line++; + + } + + if (feof (ifp)) { + + const char *filename = 0; + unsigned long line_number = 0; + + if (ll_data->read_size == 0) { + return 1; + } + + ll_data->line[pos_in_line] = '\n'; + ll_data->line[pos_in_line + 1] = '\0'; + + get_filename_and_line_number (&filename, &line_number); + + if (ll_data->new_line_number_p) { + line_number = *(ll_data->new_line_number_p); + } else { + line_number = 0; + } + + report_at (filename, line_number, REPORT_WARNING, "end of file not at end of line; newline inserted"); + + ll_data->end_of_prev_real_line = 0; + ll_data->read_size = 0; + + *line_p = ll_data->line; + *line_end_p = ll_data->line + pos_in_line; + + if (real_line_p) { + *real_line_p = ll_data->real_line; + } + + if (real_line_len_p) { + *real_line_len_p = pos_in_real_line; + } + + *newlines_p = newlines; + return 0; + + } + + } + +} + +void load_line_destroy_internal_data (void *load_line_internal_data) { + + struct load_line_data *ll_data; + + if (load_line_internal_data) { + + ll_data = load_line_internal_data; + + free (ll_data->line); + free (ll_data->real_line); + free (ll_data); + + } + +} + +void *load_line_create_internal_data (unsigned long *new_line_number_p) { + + struct load_line_data *ll_data = xmalloc (sizeof (*ll_data));; + + ll_data->capacity = 0; + ll_data->line = NULL; + ll_data->real_line = NULL; + + ll_data->read_size = 0; + ll_data->end_of_prev_real_line = 0; + + ll_data->new_line_number_p = new_line_number_p; + return ll_data; + +} diff --git a/ll.h b/ll.h new file mode 100755 index 0000000..b6754ef --- /dev/null +++ b/ll.h @@ -0,0 +1,13 @@ +/****************************************************************************** + * @file ll.h + *****************************************************************************/ +#ifndef _LL_H +#define _LL_H + +#include +int load_line (char **line_p, char **line_end_p, char **real_line_p, unsigned long *real_line_len_p, unsigned long *newlines_p, FILE *ifp, void **load_line_internal_data_p); + +void load_line_destroy_internal_data (void *load_line_internal_data); +void *load_line_create_internal_data (unsigned long *new_line_number_p); + +#endif /* _LL_H */ diff --git a/macro.c b/macro.c new file mode 100755 index 0000000..4aa45fa --- /dev/null +++ b/macro.c @@ -0,0 +1,689 @@ +/****************************************************************************** + * @file macro.c + *****************************************************************************/ +#include +#include +#include + +#include "cc.h" +#include "cstr.h" +#include "hashtab.h" +#include "lex.h" +#include "lib.h" +#include "macro.h" +#include "report.h" +#include "vector.h" + +static struct hashtab hashtab_macros = { 0 }; + +struct hashtab_name *find_macro (char *sname) { + + struct hashtab_name *key; + + if ((key = hashtab_get_key (&hashtab_macros, sname))) { + return key; + } + + return 0; + +} + +struct macro *get_macro (struct hashtab_name *key) { + return hashtab_get (&hashtab_macros, key); +} + +void add_macro (char *start, char **pp, int report_line) { + + char *sname, *caret = *pp, *arg; + unsigned int len; + + struct hashtab_name *key; + struct macro *m; + + if (is_end_of_line[(int) **pp]) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret - 1, "no macro name give in #define directive"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "no macro name give in #define directive"); + } + + return; + + } + + if (!(sname = symname (pp))) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro names must be identifiers"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "macro names must be identifiers"); + } + + return; + + } + + if (strcmp (sname, "defined") == 0) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "\"%s\" cannout be used as a macro name", sname); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "\"%s\" cannout be used as a macro name", sname); + } + + return; + + } + + if (**pp != '(' && !isspace ((int) **pp)) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "whitespace is required after macro name"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "whitespace is required after macro name"); + } + + return; + + } + + if ((key = find_macro (sname))) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "\"%s\" redefined", sname); + } else { + report_at (get_filename (), get_line_number (), REPORT_WARNING, "\"%s\" redefined", sname); + } + + if ((m = hashtab_get (&hashtab_macros, key))) { + + while ((arg = vec_pop (&m->args))) { + free (arg); + } + + free (m); + + } + + hashtab_remove (&hashtab_macros, key); + + } else { + + if (!(key = hashtab_alloc_name (sname))) { + + free (sname); + return; + + } + + } + + m = xmalloc (sizeof (*m)); + m->nargs = -1; + + m->type = MACRO_USER; + m->name = sname; + + if (**pp == '(') { + + m->nargs = 0; + (*pp)++; + + while (!is_end_of_line[(int) **pp]) { + + *pp = skip_whitespace (*pp); + + if (**pp == ')') { + break; + } + + if (m->is_variadic) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ')' after '...'"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ')' after '...'"); + } + + while ((arg = vec_pop (&m->args))) { + free (arg); + } + + free (m); + return; + + } + + arg = *pp; + + while (!is_end_of_line[(int) *arg] && !isspace ((int) *arg)) { + + if (*arg == ',' || *arg == ')') { + break; + } + + arg++; + + } + + if (arg - *pp == 3) { + + if (memcmp (*pp, "...", 3) == 0) { + + if (state->std == 90) { + report_line_at (get_filename (), get_line_number (), state->pedantic ? REPORT_ERROR : REPORT_WARNING, start, *pp, "anonymous variadic macros were introduced in C99"); + } + + m->is_variadic = 1; + + *pp = arg; + continue; + + } + + } + + if ((sname = symname (pp))) { + + vec_push (&m->args, sname); + m->nargs++; + + *pp = skip_whitespace (*pp); + + if (**pp != ',' && **pp != ')') { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ',' or, ')' after parameter"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ',' or, ')' after parameter"); + } + + goto err; + + } + + if (**pp == ')') { + break; + } + + (*pp)++; + continue; + + } + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected parameter name"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected parameter name"); + } + + goto err; + + } + + if (**pp != ')') { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "expected ')' before end of line"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected ')' before end of line"); + } + + goto err; + + } + + (*pp)++; + + } + + *pp = skip_whitespace (*pp); + + m->value = xstrdup (*pp); + len = strlen (m->value); + + if (is_end_of_line[(int) m->value[len - 1]]) { + m->value[len - 1] = '\0'; + } + + hashtab_put (&hashtab_macros, key, m); + + if (!m->is_variadic) { + + char *haystack = m->value, *needle = "__VA_ARGS__"; + char *p; + + while ((p = strstr (haystack, needle))) { + + haystack = (p + strlen (needle)); + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, m->value, p, "%s can only appear in the expansion of a variadic macro", needle); + } else { + report_at (get_filename (), get_line_number (), REPORT_WARNING, "%s can only appear in the expansion of a variadic macro", needle); + } + + } + + } + + return; + +err: + + while ((arg = vec_pop (&m->args))) { + free (arg); + } + + free (m); + return; + +} + +void remove_macro (char *start, char **pp, int report_line) { + + char *sname, *caret = *pp; + + struct hashtab_name *key; + struct macro *mp; + + if (!(sname = symname (pp))) { + + if (!(sname = symname (pp))) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro names must be identifiers"); + } else { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "macro names must be identifiers"); + } + + return; + + } + + return; + + } + + if ((key = find_macro (sname))) { + + if ((mp = hashtab_get (&hashtab_macros, key))) { + free (mp); + } + + hashtab_remove (&hashtab_macros, key); + + } + + *pp = skip_whitespace (*pp); + + if (!is_end_of_line[(int) **pp]) { + + if (report_line) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #undef directive"); + } else { + report_at (get_filename (), get_line_number (), REPORT_WARNING, "extra tokens at end of #undef directive"); + } + + } + +} + +static struct vector *get_macro_args (char *start, char *macro_name, char **pp) { + + static struct vector args_list = { 0 }; + + char *arg, saved_ch, ch; + int depth; + + memset (&args_list, 0, sizeof (args_list)); + + if (**pp != '(') { + return 0; + } + + (*pp)++; + + for (;;) { + + *pp = skip_whitespace (*pp); + + if (is_end_of_line[(int) **pp] || **pp == ')') { + break; + } + + arg = *pp; + depth = 0; + + while (!is_end_of_line[(int) **pp]) { + + ch = **pp; + + if (ch == '"' || ch == '\'') { + + (*pp)++; + + while (!is_end_of_line[(int) **pp]) { + + if (**pp == '\\') { + + (*pp)++; + + if (!is_end_of_line[(int) **pp]) { + (*pp)++; + } + + continue; + + } + + if (**pp == ch) { + + (*pp)++; + break; + + } + + (*pp)++; + + } + + continue; + + } + + if (ch == '(') { + + depth++; + + (*pp)++; + continue; + + } + + if (ch == ')') { + + if (depth == 0) { + break; + } + + depth--; + + (*pp)++; + continue; + + } + + if (ch == ',' && depth == 0) { + break; + } + + (*pp)++; + + } + + while (*pp > arg && ((*pp)[-1] == ' ' || (*pp)[-1] == '\t')) { + (*pp)--; + } + + saved_ch = **pp; + **pp = '\0'; + + vec_push (&args_list, xstrdup (arg)); + **pp = saved_ch; + + if (*(*pp = skip_whitespace (*pp)) == ',') { + (*pp)++; + } + + } + + if (**pp != ')') { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "unterminated argument list invoking macro \"%s\"", macro_name); + return 0; + + } + + (*pp)++; + return &args_list; + +} + +static char *process_value (struct macro *m, struct vector *args_list) { + + char *line, *start, *caret, *arg; + int stringify = 0, len, i, j; + + CString str; + cstr_new (&str); + + start = (line = skip_whitespace (m->value)); + +again: + + while (!is_end_of_line[(int) *line]) { + + caret = line; + + if (*line == '\"' || *line == '\'') { + + char quote = *line; + cstr_ccat (&str, *line++); + + while (!is_end_of_line[(int) *line]) { + + cstr_ccat (&str, *line); + + if (*line == '\\') { + + line++; + + if (!is_end_of_line[(int) *line]) { + cstr_ccat (&str, *line); + } + + } else if (*line == quote) { + + line++; + break; + + } + + line++; + + } + + continue; + + } + + if (line[0] == '#') { + + if (stringify) { + + cstr_ccat (&str, '"'); + stringify = 0; + + } + + if (line[1] == '#') { + + while (str.size && ((char *) str.data)[str.size - 1] == ' ') { + str.size--; + } + + line = skip_whitespace (line + 2); + + } else { + + line = skip_whitespace (line + 1); + stringify = 1; + + } + + continue; + + } + + if (is_name_beginner ((int) *line)) { + + len = strlen (arg = symname (&line)); + + if (args_list) { + + if (strcmp (arg, "__VA_ARGS__") == 0) { + + if (stringify) { + cstr_ccat (&str, '"'); + } + + for (i = m->nargs; i < args_list->length; i++) { + + len = strlen (arg = args_list->data[i]); + + for (j = 0; j < len; j++ ) { + + if (arg[j] == '"') { + + cstr_ccat (&str, '\\'); + cstr_ccat (&str, '"'); + + } else { + cstr_ccat (&str, arg[j]); + } + + } + + if (i < args_list->length - 1) { + + cstr_ccat (&str, ','); + cstr_ccat (&str, ' '); + + } + + } + + if (stringify) { + + cstr_ccat (&str, '"'); + stringify = 0; + + } + + continue; + + } + + for (i = 0; i < m->nargs; i++) { + + if (strcmp (m->args.data[i], arg) == 0) { + + len = strlen (arg = args_list->data[i]); + + if (stringify) { + + cstr_ccat (&str, '"'); + + for (i = 0; i < len; i++ ) { + + if (arg[i] == '"') { + + cstr_ccat (&str, '\\'); + cstr_ccat (&str, '"'); + + } else { + cstr_ccat (&str, arg[i]); + } + + } + + cstr_ccat (&str, '"'); + stringify = 0; + + } else { + cstr_cat (&str, arg, len); + } + + goto again; + + } + + } + + } + + if (stringify) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "'#' is not followed by a macro paremeter"); + break; + + } + + cstr_cat (&str, arg, len); + + } else { + + if (isprint ((int) *line) && stringify) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "'#' is not followed by a macro paremeter"); + break; + + } + + cstr_ccat (&str, *line); + line++; + + } + + } + + cstr_ccat (&str, '\0'); + return xstrdup (str.data); + +} + +char *process_macro (char *start, char **pp, struct macro *m) { + + struct vector *args_list = 0; + char *caret; + + if (m->nargs >= 0 || m->is_variadic) { + + args_list = 0; + caret = *pp; + + if ((args_list = get_macro_args (start, m->name, pp))) { + + if (args_list->length < m->nargs) { + + char *tmp = (m->nargs == 1 ? " argument" : " arguments"); + char *tmp2 = (args_list->length == 1 ? "only " : ""); + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro \"%s\" requires %d%s, but %s%d given", m->name, m->nargs, tmp, tmp2, args_list->length); + + } else if (args_list->length > m->nargs) { + + if (!m->is_variadic) { + + char *tmp = (args_list->length == 1 ? " argument" : " arguments"); + char *tmp2 = (m->nargs == 1 ? "just " : ""); + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "macro \"%s\" passed %d%s, but takes %s%d", m->name, args_list->length, tmp, tmp2, m->nargs); + + } + + } + + } + + } + + return process_value (m, args_list); + +} + +void push_macro (struct hashtab_name *key, struct macro *m) { + hashtab_put (&hashtab_macros, key, m); +} diff --git a/macro.h b/macro.h new file mode 100755 index 0000000..68f1095 --- /dev/null +++ b/macro.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * @file macro.h + *****************************************************************************/ +#ifndef _MACRO_H +#define _MACRO_H + +#include "vector.h" + +#define MACRO_BUILTIN 0 +#define MACRO_USER 1 + +struct macro { + + char *name, *value; + int is_variadic, type; + + struct vector args; + int nargs; + +}; + +#include "hashtab.h" +struct hashtab_name *find_macro (char *sname); +struct macro *get_macro (struct hashtab_name *key); + +void add_macro (char *start, char **pp, int report_line); +void remove_macro (char *start, char **pp, int report_line); + +char *process_macro (char *start, char **pp, struct macro *m); +void push_macro (struct hashtab_name *key, struct macro *m); + +#endif /* _MACRO_H */ diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..45a2795 --- /dev/null +++ b/parse.c @@ -0,0 +1,33959 @@ +/****************************************************************************** + * @file parse.c + *****************************************************************************/ +#include +#include +#include + +#include "cc.h" +#include "expr.h" +#include "lib.h" +#include "parse.h" +#include "report.h" +#include "token.h" + +#define SECTION_NONE 0 +#define SECTION_TEXT 1 +#define SECTION_DATA 2 +#define SECTION_BSS 3 + +static int current_section = 0; +static int anon_label = 1; +static int anonymous_aggregate_owner_id = 1; + +static int pending_return_jump = 0; +static int local_static_id = 1; + +#define MAX_PENDING_STATEMENT_LABELS 256 + +static int pending_statement_labels[MAX_PENDING_STATEMENT_LABELS]; +static int pending_statement_label_count = 0; + +static void flush_pending_statement_labels (void); + +#define DATA_NONE 0 +#define DATA_VOID 4 +#define DATA_PTR 4 + +static int current_return_label = 0; + +#define DATA_CHAR (1 | (1 << 5)) +#define DATA_SHORT (2 | (1 << 6)) +#define DATA_INT (4 | (1 << 7)) +#define DATA_LONG (4 | (1 << 8)) +#define DATA_LLONG (8 | (1 << 9)) +#define DATA_FLOAT (4 | (1 << 10)) +#define DATA_DOUBLE (8 | (1 << 11)) + +static int index_step_size (int size) { + + if (size == DATA_CHAR || size == DATA_SHORT || size == DATA_INT || + size == DATA_LONG || size == DATA_LLONG || size == DATA_FLOAT || + size == DATA_DOUBLE) { + return size & 0x1f; + } + + if (size <= 0) { + return DATA_INT & 0x1f; + } + + return size; + +} + +static int rhs_last_pointer_depth = 0; +static int rhs_last_pointed_size = 0; + +static void set_rhs_last_pointer_info (int depth, int size) { + + rhs_last_pointer_depth = depth; + rhs_last_pointed_size = depth > 1 ? DATA_PTR : (size > 0 ? size : (DATA_INT & 0x1f)); + +} + +static void clear_rhs_last_pointer_info (void) { + + rhs_last_pointer_depth = 0; + rhs_last_pointed_size = 0; + +} + +static int current_function_has_return_statement = 0; +static int current_parse_block_depth = 0; +static int current_function_is_void = 0; +static int current_function_is_floating = 0; +static int current_function_return_size = DATA_NONE; +static int current_function_return_is_unsigned = 0; +static int current_function_returns_aggregate = 0; + +static struct local_symbol *pending_struct_return_lhs = 0; +static const char *pending_struct_return_global_name = 0; + +static int pending_struct_return_stack_address = 0; +static int pending_struct_return_stack_offset = 0; +static int pending_struct_return_stack_top = 0; + +static int suppress_next_struct_return_scalar_store = 0; +static int token_identifier_is_function_call_rhs_now (void); + +static int parsed_type_size = DATA_NONE; +static int parsed_type_is_inline = 0; +static int parsed_type_is_void = 0; +static int parsed_type_is_unsigned = 0; +static int parsed_type_is_floating = 0; +static int parsed_type_only_qualifiers = 0; + +static int parsed_type_is_aggregate = 0; +static int parsed_type_has_tag = 0; +static char parsed_type_tag_name[128]; + +static char last_cast_type_tag_name[128]; +static int last_cast_type_object_size = 0; + +static int parsed_type_is_array_typedef = 0; +static int parsed_type_array_element_size = DATA_NONE; + +static long parsed_type_array_count = 1; + +static int declarator_has_function = 0; +static int declarator_function_is_pointer = 0; +static int declarator_function_param_count = 0; +static int declarator_function_has_prototype = 0; +static int declarator_function_is_variadic = 0; +static int declarator_array_unsized = 0; + +static int declarator_is_pointer = 0; +static int declarator_pointer_depth = 0; +static int declarator_has_array = 0; + +static int global_initializer_accept_symbol_addresses = 0; + +static long declarator_array_count = 1; +static long declarator_last_array_count = 1; + +#define STORAGE_NONE 0 +#define STORAGE_EXTERN 1 +#define STORAGE_STATIC 2 +#define STORAGE_TYPEDEF 3 + +static int parsed_storage_class = STORAGE_NONE; + +#define MAX_GLOBAL_INIT_FIELDS 262144 +#define MAX_AGG_FIELDS 512 + +static int parsed_field_sizes[MAX_AGG_FIELDS]; +static int parsed_field_count = 0; + +#define MAX_STRING_INIT_BYTES 4096 + +static void clear_parsed_fields (void) { + parsed_field_count = 0; +} + +static void append_parsed_field (int size) { + + if (parsed_field_count < MAX_AGG_FIELDS) { + parsed_field_sizes[parsed_field_count++] = size; + } + +} + +static int fields_storage_size (const int *sizes, int count) { + + int total = 0, i; + + for (i = 0; i < count; i++) { + + if (sizes[i] < 0) { + total += -sizes[i]; + } else { + total += sizes[i]; + } + + } + + return total; + +} + +#define MAX_MEMBER_INFOS 4096 + +struct member_info_entry { + + char *name; + int offset; + int size; + int elem_size; + int pointer_depth; + + int is_array; + int is_floating; + int is_unsigned; + + char *tag_name; + char *owner_tag_name; + + int owner_size; + +}; + +static struct member_info_entry member_infos[MAX_MEMBER_INFOS]; +static int member_info_count = 0; + +static int postfix_member_pointer_depth = 0; +static int postfix_member_pointed_size = 0; +static int postfix_member_seen = 0; +static int postfix_copy_lvalue_size = 0; + +static const char *postfix_copy_lvalue_tag_name = 0; + +static const char *last_found_member_tag_name = 0; +static int last_found_member_is_unsigned = 0; + +static int postfix_member_offset = 0; +static int postfix_member_size = 0; + +static int postfix_member_is_floating = 0; +static int postfix_member_is_unsigned = 0; + +static void remember_member_info_ex (const char *name, int offset, int size, int elem_size, int pointer_depth, int is_array, int is_floating) { + + if (!name) { + return; + } + + if (member_info_count >= MAX_MEMBER_INFOS) { + return; + } + + member_infos[member_info_count].name = xstrdup (name); + member_infos[member_info_count].offset = offset; + member_infos[member_info_count].size = size; + member_infos[member_info_count].elem_size = elem_size > 0 ? elem_size : size; + member_infos[member_info_count].pointer_depth = pointer_depth; + member_infos[member_info_count].is_array = is_array; + member_infos[member_info_count].is_floating = is_floating ? 1 : 0; + member_infos[member_info_count].is_unsigned = parsed_type_is_unsigned ? 1 : 0; + member_infos[member_info_count].tag_name = parsed_type_tag_name[0] ? xstrdup (parsed_type_tag_name) : 0; + member_infos[member_info_count].owner_size = 0; + member_infos[member_info_count].owner_tag_name = 0; + + member_info_count++; + +} + +static int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) { + + int best; + int i; + + last_found_member_tag_name = 0; + last_found_member_is_unsigned = 0; + + if (!name) { + return 0; + } + + best = -1; + + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) { + + best = i; + break; + + } + + } + + if (best < 0) { + return 0; + } + + last_found_member_tag_name = member_infos[best].tag_name; + last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0; + + if (offset) { + *offset = member_infos[best].offset; + } + + if (size) { + *size = member_infos[best].size; + } + + if (elem_size) { + *elem_size = member_infos[best].elem_size; + } + + if (pointer_depth) { + *pointer_depth = member_infos[best].pointer_depth; + } + + if (is_array) { + *is_array = member_infos[best].is_array; + } + + if (is_floating) { + *is_floating = member_infos[best].is_floating; + } + + return 1; + +} + +static int find_member_info (const char *name, int *offset, int *size) { + return find_member_info_ex (name, offset, size, 0, 0, 0, 0); +} + +static int find_member_info_ex_bounded (const char *name, int max_size, const char *owner_tag_name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) { + + int best; + int i; + + last_found_member_tag_name = 0; + last_found_member_is_unsigned = 0; + + if (!name) { + return 0; + } + + if (max_size <= 0 && (!owner_tag_name || !owner_tag_name[0])) { + return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating); + } + + best = -1; + + if (owner_tag_name && owner_tag_name[0]) { + + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0 + && member_infos[i].owner_tag_name + && strcmp (member_infos[i].owner_tag_name, owner_tag_name) == 0) { + + best = i; + break; + + } + + } + + } + + if (best < 0) { + + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0 + && member_infos[i].owner_size == max_size) { + + if (best < 0 || member_infos[i].offset < member_infos[best].offset) { + best = i; + } + + } + + } + + } + + if (best < 0) { + + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0 + && member_infos[i].offset + member_infos[i].size <= max_size) { + + if (best < 0 || member_infos[i].offset < member_infos[best].offset) { + best = i; + } + + } + + } + + } + + if (best >= 0) { + + last_found_member_tag_name = member_infos[best].tag_name; + last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0; + + if (offset) { + *offset = member_infos[best].offset; + } + + if (size) { + *size = member_infos[best].size; + } + + if (elem_size) { + *elem_size = member_infos[best].elem_size; + } + + if (pointer_depth) { + *pointer_depth = member_infos[best].pointer_depth; + } + + if (is_array) { + *is_array = member_infos[best].is_array; + } + + if (is_floating) { + *is_floating = member_infos[best].is_floating; + } + + return 1; + + } + + return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating); + +} + +static const char *find_member_tag_name (const char *name) { + + int i; + + if (!name) { + return 0; + } + + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) { + return member_infos[i].tag_name; + } + + } + + return 0; + +} + +#define MAX_AGG_TAGS 256 + +struct aggregate_tag_entry { + + char *name; + + int is_union; + int size; + + int field_count; + int field_sizes[MAX_AGG_FIELDS]; + +}; + +static struct aggregate_tag_entry aggregate_tags[MAX_AGG_TAGS]; +static int aggregate_tag_count = 0; + +static struct aggregate_tag_entry *find_aggregate_tag (const char *name, int is_union) { + + int i; + + if (!name) { + return 0; + } + + for (i = 0; i < aggregate_tag_count; i++) { + + if (aggregate_tags[i].is_union == is_union && strcmp (aggregate_tags[i].name, name) == 0) { + return &aggregate_tags[i]; + } + + } + + return 0; + +} + +static void save_aggregate_tag (const char *name, int is_union, int size, const int *field_sizes, int field_count) { + + struct aggregate_tag_entry *entry; + int i; + + if (!name) { + return; + } + + entry = find_aggregate_tag (name, is_union); + + if (!entry) { + + if (aggregate_tag_count >= MAX_AGG_TAGS) { + return; + } + + entry = &aggregate_tags[aggregate_tag_count++]; + entry->name = xstrdup (name); + entry->is_union = is_union; + + } + + entry->size = size; + entry->field_count = 0; + + for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) { + entry->field_sizes[entry->field_count++] = field_sizes[i]; + } + +} + +static void load_aggregate_tag_fields (struct aggregate_tag_entry *entry) { + + int i; + clear_parsed_fields (); + + if (!entry) { + return; + } + + parsed_type_size = entry->size; + parsed_type_is_aggregate = 1; + parsed_type_is_void = 0; + parsed_type_has_tag = 1; + + for (i = 0; i < entry->field_count; i++) { + append_parsed_field (entry->field_sizes[i]); + } + +} + +#define MAX_TYPEDEF_NAMES 512 + +struct typedef_entry { + + char *name; + int size; + + int is_aggregate; + int is_unsigned; + int is_void; + + int field_count; + int field_sizes[MAX_AGG_FIELDS]; + + int array_element_size; + int is_array; + + char *tag_name; + long array_count; + +}; + +static struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES]; +static int typedef_name_count = 0; + +static void clear_typedef_names (void) { + + int i; + + for (i = 0; i < typedef_name_count; i++) { + + free (typedef_names[i].name); + + typedef_names[i].name = 0; + typedef_names[i].size = 0; + + typedef_names[i].tag_name = 0; + + typedef_names[i].is_aggregate = 0; + typedef_names[i].is_unsigned = 0; + + typedef_names[i].field_count = 0; + typedef_names[i].is_array = 0; + typedef_names[i].array_count = 1; + typedef_names[i].array_element_size = DATA_NONE; + + } + + typedef_name_count = 0; + +} + +static struct typedef_entry *find_typedef_name (const char *name) { + + int i; + + if (!name) { + return 0; + } + + for (i = 0; i < typedef_name_count; i++) { + + if (strcmp (typedef_names[i].name, name) == 0) { + return &typedef_names[i]; + } + + } + + return 0; + +} + +struct local_symbol; +static struct local_symbol *find_local_symbol (const char *name); + +static int is_current_typedef_name (void) { + + if (tok.kind != TOK_IDENT || !tok.ident) { + return 0; + } + + if (find_local_symbol (tok.ident)) { + return 0; + } + + return find_typedef_name (tok.ident) != 0; + +} + +static void save_typedef_name (const char *name, int size, int is_unsigned, int is_void, int is_aggregate, int is_array, long array_count, int array_element_size, const int *field_sizes, int field_count) { + + struct typedef_entry *entry; + int i; + + if (!name || !*name) { + return; + } + + entry = find_typedef_name (name); + + if (!entry) { + + if (typedef_name_count >= MAX_TYPEDEF_NAMES) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many typedef names"); + return; + + } + + entry = &typedef_names[typedef_name_count++]; + entry->name = xstrdup (name); + entry->tag_name = 0; + + } + + if (entry->tag_name) { + + free (entry->tag_name); + entry->tag_name = 0; + + } + + if (parsed_type_tag_name[0]) { + entry->tag_name = xstrdup (parsed_type_tag_name); + } else if (is_aggregate && name && *name) { + entry->tag_name = xstrdup (name); + } + + entry->is_void = is_void; + entry->size = size; + + entry->is_unsigned = is_unsigned; + entry->is_aggregate = is_aggregate; + + entry->field_count = 0; + entry->is_array = is_array ? 1 : 0; + entry->array_count = array_count > 0 ? array_count : 1; + entry->array_element_size = array_element_size > 0 ? array_element_size : DATA_INT; + + for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) { + entry->field_sizes[entry->field_count++] = field_sizes[i]; + } + + if (is_aggregate && !parsed_type_tag_name[0] && name && *name) { + + int mi; + + for (mi = 0; mi < member_info_count; mi++) { + + if (member_infos[mi].owner_size == size + && member_infos[mi].owner_tag_name == 0) { + member_infos[mi].owner_tag_name = xstrdup (name); + } + + } + + } + +} + +static void update_typedef_name_from_aggregate_tag (const char *name, int size, const int *field_sizes, int field_count) { + + struct typedef_entry *entry; + int i; + + if (!name) { + return; + } + + entry = find_typedef_name (name); + + if (!entry) { + return; + } + + entry->size = size; + entry->is_aggregate = 1; + entry->is_void = 0; + entry->is_array = 0; + entry->array_count = 1; + entry->array_element_size = DATA_NONE; + entry->field_count = 0; + + if (entry->tag_name) { + + free (entry->tag_name); + entry->tag_name = 0; + + } + + if (name && *name) { + entry->tag_name = xstrdup (name); + } + + for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) { + entry->field_sizes[entry->field_count++] = field_sizes[i]; + } + +} + +static void load_typedef_name (struct typedef_entry *entry) { + + int i; + clear_parsed_fields (); + + if (!entry) { + + parsed_type_size = DATA_INT & 0x1f; + + parsed_type_is_aggregate = 0; + parsed_type_is_unsigned = 0; + parsed_type_is_void = 0; + parsed_type_is_floating = 0; + parsed_type_is_array_typedef = 0; + parsed_type_array_count = 1; + parsed_type_array_element_size = DATA_NONE; + + append_parsed_field (DATA_INT & 0x1f); + return; + + } + + parsed_type_size = entry->size; + + parsed_type_is_aggregate = entry->is_aggregate; + parsed_type_is_unsigned = entry->is_unsigned; + parsed_type_is_void = entry->is_void; + parsed_type_is_floating = 0; + parsed_type_is_array_typedef = entry->is_array; + parsed_type_array_count = entry->array_count > 0 ? entry->array_count : 1; + parsed_type_array_element_size = entry->array_element_size > 0 ? entry->array_element_size : DATA_INT; + + parsed_type_has_tag = 0; + parsed_type_tag_name[0] = '\0'; + + if (entry->tag_name && entry->tag_name[0]) { + + strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1); + parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0'; + + parsed_type_has_tag = 1; + + } + + for (i = 0; i < entry->field_count; i++) { + append_parsed_field (entry->field_sizes[i]); + } + +} + +#define MAX_ENUM_CONSTANTS 4096 + +struct enum_const_entry { + + char *name; + int64_s value; + +}; + +static struct enum_const_entry enum_constants[MAX_ENUM_CONSTANTS]; +static int enum_constant_count = 0; + +static struct enum_const_entry *find_enum_constant (const char *name) { + + int i; + + if (!name) { + return 0; + } + + for (i = 0; i < enum_constant_count; i++) { + + if (strcmp (enum_constants[i].name, name) == 0) { + return &enum_constants[i]; + } + + } + + return 0; + +} + +static void save_enum_constant (const char *name, int64_s value, const char *start, const char *caret) { + + struct enum_const_entry *entry; + + if (!name || !*name) { + return; + } + + entry = find_enum_constant (name); + + if (entry) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "duplicate enum constant '%s'", name); + return; + + } + + if (enum_constant_count >= MAX_ENUM_CONSTANTS) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "too many enum constants"); + return; + + } + + entry = &enum_constants[enum_constant_count++]; + entry->name = xstrdup (name); + entry->value = value; + +} + +int resolve_enum_constant (const char *name, int64_s *out) { + + struct enum_const_entry *entry = find_enum_constant (name); + + if (!entry) { + return 0; + } + + if (out) { + *out = entry->value; + } + + return 1; + +} + +static void clear_enum_constants (void) { + + int i; + + for (i = 0; i < enum_constant_count; i++) { + + free (enum_constants[i].name); + + enum_constants[i].name = 0; + enum_constants[i].value.low = 0; + enum_constants[i].value.high = 0; + + } + + enum_constant_count = 0; + +} + +#define MAX_GLOBAL_SYMBOLS 4096 + +#define GLOBAL_SYMBOL_OBJECT 1 +#define GLOBAL_SYMBOL_FUNCTION 2 + +struct global_symbol_entry { + + char *name; + + int is_unsigned, is_extern; + int kind, size; + + int array_element_size; + int is_array; + + long array_count; + + int is_floating; + int pointer_depth; + int pointed_size; + int returns_void; + + int param_count; + int has_prototype; + int is_variadic; + + int is_implicit; + int extern_emitted; + + char *tag_name; + +}; + +static struct global_symbol_entry global_symbols[MAX_GLOBAL_SYMBOLS]; +static int global_symbol_count = 0; + +static const char *last_declarator_name_start = 0; +static const char *last_declarator_name_caret = 0; + +static unsigned long last_declarator_name_line = 0; + +static int capture_declarator_name_location = 0; +static int captured_declarator_name_location = 0; + +static const char *captured_declarator_name_start = 0; +static const char *captured_declarator_name_caret = 0; + +static unsigned long captured_declarator_name_line = 0; + +static void clear_global_symbols (void) { + + int i; + + for (i = 0; i < global_symbol_count; i++) { + + free (global_symbols[i].name); + + global_symbols[i].name = 0; + global_symbols[i].kind = 0; + global_symbols[i].size = 0; + + global_symbols[i].array_element_size = 0; + global_symbols[i].is_array = 0; + + global_symbols[i].is_extern = 0; + global_symbols[i].is_unsigned = 0; + global_symbols[i].is_floating = 0; + global_symbols[i].returns_void = 0; + global_symbols[i].param_count = 0; + global_symbols[i].has_prototype = 0; + global_symbols[i].is_variadic = 0; + global_symbols[i].is_implicit = 0; + global_symbols[i].extern_emitted = 0; + + if (global_symbols[i].tag_name) { + + free (global_symbols[i].tag_name); + global_symbols[i].tag_name = 0; + + } + + } + + global_symbol_count = 0; + +} + +static int find_global_symbol (const char *name) { + + int i; + + if (!name) { + return -1; + } + + for (i = 0; i < global_symbol_count; i++) { + + if (strcmp (global_symbols[i].name, name) == 0) { + return i; + } + + } + + return -1; + +} + +static int asm_symbol_is_internal (const char *name) { + + if (!name || !*name) { + return 1; + } + + if (name[0] == '.') { + return 1; + } + + if (name[0] == 'L') { + + if (name[1] >= '0' && name[1] <= '9') { + return 1; + } + + if (name[1] == 'C' && name[2] >= '0' && name[2] <= '9') { + return 1; + } + + } + + return 0; + +} + +static const char *asm_global_symbol_name (const char *name) { + + static char buffers[8][512]; + static int index = 0; + + char *out; + unsigned long len; + + if (asm_symbol_is_internal (name)) { + return name; + } + + index = (index + 1) & 7; + + out = buffers[index]; + len = strlen (name); + + if (len > sizeof (buffers[0]) - 2) { + len = sizeof (buffers[0]) - 2; + } + + if (!state->no_leading_underscore) { + + out[0] = '_'; + + memcpy (out + 1, name, len); + out[len + 1] = 0; + + } else { + + memcpy (out, name, len); + out[len] = 0; + + } + + return out; + +} + +static int get_global_symbol_kind (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].kind; + } + + return 0; + +} + +static void set_global_symbol_unsigned (const char *name, int is_unsigned) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].is_unsigned = is_unsigned ? 1 : 0; + } + +} + +static int get_global_symbol_unsigned (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_unsigned; + } + + return 0; + +} + +static void set_global_symbol_array (const char *name, int is_array) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].is_array = is_array ? 1 : 0; + } + +} + +static int get_global_symbol_array (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_array; + } + + return 0; + +} + +static void set_global_symbol_array_count (const char *name, long array_count) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].array_count = array_count; + } + +} + +static long get_global_symbol_array_count (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].array_count; + } + + return 0; + +} + +static void set_global_symbol_array_element_size (const char *name, int elem_size) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].array_element_size = elem_size > 0 ? elem_size : 0; + } + +} + +static int get_global_symbol_array_element_size (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + if (global_symbols[i].array_element_size > 0) { + return global_symbols[i].array_element_size; + } + + if (global_symbols[i].tag_name && global_symbols[i].tag_name[0]) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (global_symbols[i].tag_name, 0); + + if (entry && entry->size > 0) { + return entry->size; + } + + } + + } + + return 0; + +} + +static void set_global_symbol_floating (const char *name, int is_floating) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].is_floating = is_floating ? 1 : 0; + } + +} + +static int get_global_symbol_floating (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_floating; + } + + return 0; + +} + +static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + global_symbols[i].pointer_depth = pointer_depth; + global_symbols[i].pointed_size = pointed_size; + + } + +} + +static int get_global_symbol_pointer_depth (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].pointer_depth; + } + + return 0; + +} + +static int get_global_symbol_pointed_size (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0 && global_symbols[i].pointed_size > 0) { + return global_symbols[i].pointed_size; + } + + return DATA_INT & 0x1f; + +} + +static void set_global_symbol_tag_name (const char *name, const char *tag_name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + if (global_symbols[i].tag_name) { + + free (global_symbols[i].tag_name); + global_symbols[i].tag_name = 0; + + } + + if (tag_name && tag_name[0]) { + global_symbols[i].tag_name = xstrdup (tag_name); + } + + } + +} + +static const char *get_global_symbol_tag_name (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].tag_name; + } + + return 0; + +} + +static void set_global_symbol_returns_void (const char *name, int returns_void) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].returns_void = returns_void ? 1 : 0; + } + +} + +static int get_global_symbol_returns_void (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].returns_void; + } + + return 0; + +} + +static void set_global_symbol_param_count (const char *name, int param_count, int has_prototype, int is_variadic) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + global_symbols[i].param_count = param_count < 0 ? 0 : param_count; + global_symbols[i].has_prototype = has_prototype ? 1 : 0; + global_symbols[i].is_variadic = is_variadic ? 1 : 0; + + } + +} + +static int get_global_symbol_has_prototype (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].has_prototype; + } + + return 0; + +} + +static int get_global_symbol_is_variadic (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_variadic; + } + + return 0; + +} + +static int get_global_symbol_param_count (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].param_count; + } + + return 0; + +} + +static void set_global_symbol_size (const char *name, int size) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].size = size; + } + +} + +static int get_global_symbol_size (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0 && global_symbols[i].size > 0) { + return global_symbols[i].size; + } + + return DATA_INT & 0x1f; + +} + +static int add_global_symbol (const char *name, int kind, int is_extern, const char *line_start, const char *name_caret, unsigned long lineno) { + + int old; + + if (!name || !*name) { + return 0; + } + + if (!line_start) { + line_start = tok.start; + } + + if (!name_caret) { + name_caret = tok.caret; + } + + if (lineno == 0) { + lineno = get_line_number (); + } + + old = find_global_symbol (name); + + if (old >= 0) { + + if (global_symbols[old].is_implicit) { + + global_symbols[old].is_extern = is_extern ? 1 : 0; + global_symbols[old].is_implicit = 0; + + global_symbols[old].kind = kind; + return 1; + + } + + if (is_extern) { + return 0; + } + + if (global_symbols[old].is_extern) { + + global_symbols[old].kind = kind; + global_symbols[old].is_extern = 0; + + return 1; + + } + + report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "duplicate symbol '%s'", name); + return 0; + + } + + if (global_symbol_count >= MAX_GLOBAL_SYMBOLS) { + + report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "too many global symbols"); + return 0; + + } + + global_symbols[global_symbol_count].name = xstrdup (name); + global_symbols[global_symbol_count].kind = kind; + global_symbols[global_symbol_count].is_extern = is_extern; + global_symbols[global_symbol_count].is_unsigned = 0; + global_symbols[global_symbol_count].is_floating = 0; + global_symbols[global_symbol_count].is_array = 0; + global_symbols[global_symbol_count].returns_void = 0; + global_symbols[global_symbol_count].param_count = 0; + global_symbols[global_symbol_count].has_prototype = 0; + global_symbols[global_symbol_count].is_variadic = 0; + global_symbols[global_symbol_count].is_implicit = 0; + global_symbols[global_symbol_count].extern_emitted = 0; + global_symbols[global_symbol_count].tag_name = 0; + + global_symbol_count++; + return 1; + +} + +static void ensure_global_function_symbol (const char *name, const char *line_start, const char *name_caret, unsigned long lineno) { + + int i; + + if (!name || !*name) { + return; + } + + i = find_global_symbol (name); + + if (i >= 0) { + return; + } + + report_line_at (get_filename (), lineno, REPORT_WARNING, line_start, name_caret, "implicit declaration of function '%s'", name); + + if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, line_start, name_caret, lineno)) { + + i = find_global_symbol (name); + + if (i >= 0) { + + global_symbols[i].size = DATA_INT & 0x1f; + global_symbols[i].is_unsigned = 0; + global_symbols[i].is_floating = 0; + global_symbols[i].returns_void = 0; + global_symbols[i].param_count = 0; + global_symbols[i].has_prototype = 0; + global_symbols[i].is_variadic = 0; + global_symbols[i].is_implicit = 1; + + } + + } + +} + +#define MAX_INLINE_FUNCTIONS 256 + +struct inline_function_entry { + + char *name; + char *body; + char *data; + + int param_count; + int has_prototype; + int returns_void; + int is_floating; + int return_size; + int return_label; + int data_emitted; + int expanding; + + int usable; + +}; + +static struct inline_function_entry inline_functions[MAX_INLINE_FUNCTIONS]; +static int inline_function_count = 0; + +static void clear_inline_functions (void) { + + int i; + + for (i = 0; i < inline_function_count; i++) { + + free (inline_functions[i].name); + free (inline_functions[i].body); + free (inline_functions[i].data); + + inline_functions[i].name = 0; + inline_functions[i].body = 0; + inline_functions[i].data = 0; + inline_functions[i].param_count = 0; + inline_functions[i].has_prototype = 0; + inline_functions[i].returns_void = 0; + inline_functions[i].is_floating = 0; + inline_functions[i].return_size = 0; + inline_functions[i].return_label = 0; + inline_functions[i].data_emitted = 0; + inline_functions[i].expanding = 0; + inline_functions[i].usable = 0; + + } + + inline_function_count = 0; + +} + +static int find_inline_function (const char *name) { + + int i; + + if (!name) { + return -1; + } + + for (i = 0; i < inline_function_count; i++) { + + if (strcmp (inline_functions[i].name, name) == 0) { + return i; + } + + } + + return -1; + +} + +static void remember_inline_function_signature (const char *name, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) { + + int i; + + if (!name || !*name) { + return; + } + + i = find_inline_function (name); + + if (i < 0) { + + if (inline_function_count >= MAX_INLINE_FUNCTIONS) { + return; + } + + i = inline_function_count++; + inline_functions[i].name = xstrdup (name); + inline_functions[i].body = 0; + inline_functions[i].data = 0; + inline_functions[i].data_emitted = 0; + inline_functions[i].expanding = 0; + + } + + inline_functions[i].param_count = param_count < 0 ? 0 : param_count; + inline_functions[i].has_prototype = has_prototype ? 1 : 0; + inline_functions[i].returns_void = returns_void ? 1 : 0; + inline_functions[i].is_floating = is_floating ? 1 : 0; + inline_functions[i].return_size = return_size; + inline_functions[i].return_label = 0; + inline_functions[i].data_emitted = 0; + inline_functions[i].expanding = 0; + inline_functions[i].usable = 0; + +} + +static int inline_asm_body_is_safe (const char *body) { + + const char *p = body; + + if (!body) { + return 0; + } + + while (*p) { + + const char *line = p; + const char *eol = strchr (p, '\n'); + + size_t len = eol ? (size_t) (eol - line) : strlen (line); + + while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) { + len--; + } + + while (len > 0 && (*line == ' ' || *line == '\t')) { + + line++; + len--; + + } + + if (len >= 4 && strncmp (line, "ret", 3) == 0) { + return 0; + } + + if (len >= 5 && strncmp (line, "leave", 5) == 0) { + return 0; + } + + if (len > 0 && line[0] == '.' && !(len >= 3 && line[1] == 'L' && line[2] >= '0' && line[2] <= '9')) { + + if (!((len == 5 && strncmp (line, ".data", 5) == 0) || + (len == 6 && strncmp (line, ".data?", 6) == 0) || + (len == 5 && strncmp (line, ".code", 5) == 0) || + (len == 5 && strncmp (line, ".text", 5) == 0) || + (len == 4 && strncmp (line, ".bss", 4) == 0))) { + + return 0; + + } + + } + + if (len >= 6 && strncmp (line, "public", 6) == 0) { + return 0; + } + + if (!eol) { + break; + } + + p = eol + 1; + + } + + return 1; + +} + +static char *extract_inline_asm_body (const char *asm_text, int return_label) { + + char marker[64]; + char *body; + + const char *start, *end; + size_t len; + + if (!asm_text) { + return 0; + } + + start = strstr (asm_text, " mov ebp, esp\n"); + + if (start) { + start += strlen (" mov ebp, esp\n"); + } else { + + start = strstr (asm_text, " movl %esp, %ebp\n"); + + if (start) { + start += strlen (" movl %esp, %ebp\n"); + } + + } + + if (!start) { + return 0; + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (marker, "\nL%d:\n", return_label); + } else { + sprintf (marker, "\n.L%d:\n", return_label); + } + + end = strstr (start, marker); + + if (!end) { + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (marker, "L%d:\n", return_label); + } else { + sprintf (marker, ".L%d:\n", return_label); + } + + if (strncmp (start, marker, strlen (marker)) == 0) { + end = start; + } else { + return 0; + } + + } + + if (*start == '\n') { + start++; + } + + len = (size_t) (end - start); + + while (len > 0 && (start[len - 1] == '\r' || start[len - 1] == '\n')) { + len--; + } + + body = xmalloc (len + 2); + memcpy (body, start, len); + + body[len++] = '\n'; + body[len] = 0; + + if (!inline_asm_body_is_safe (body)) { + + free (body); + return 0; + + } + + return body; + +} + +static char *read_tmp_file_text (FILE *fp) { + + long len; + char *text; + + if (!fp) { + return 0; + } + + fflush (fp); + + if (fseek (fp, 0, SEEK_END) != 0) { + return 0; + } + + len = ftell (fp); + + if (len < 0) { + return 0; + } + + if (fseek (fp, 0, SEEK_SET) != 0) { + return 0; + } + + text = xmalloc ((size_t) len + 1); + + if (len > 0 && fread (text, 1, (size_t) len, fp) != (size_t) len) { + free (text); + return 0; + } + + text[len] = 0; + return text; + +} + +static void append_inline_text (char **dst, const char *start, size_t len) { + + size_t old_len = 0; + char *out; + + if (!dst || !start || len == 0) { + return; + } + + if (*dst) { + old_len = strlen (*dst); + } + + out = xmalloc (old_len + len + 1); + + if (old_len) { + memcpy (out, *dst, old_len); + free (*dst); + } + + memcpy (out + old_len, start, len); + out[old_len + len] = 0; + *dst = out; + +} + +static void split_inline_asm_sections (char **bodyp, char **datap) { + + const char *p; + + char *body = 0; + char *data = 0; + + int in_data = 0; + + if (!bodyp || !*bodyp) { + return; + } + + p = *bodyp; + + while (*p) { + + const char *line = p; + const char *eol = strchr (p, '\n'); + const char *next = eol ? eol + 1 : p + strlen (p); + + size_t len = (size_t) (next - line); + + const char *trim = line; + size_t tlen = eol ? (size_t) (eol - line) : strlen (line); + + while (tlen > 0 && (trim[tlen - 1] == '\r' || trim[tlen - 1] == '\n')) { + tlen--; + } + + while (tlen > 0 && (*trim == ' ' || *trim == '\t')) { + + trim++; + tlen--; + + } + + if ((tlen == 5 && strncmp (trim, ".data", 5) == 0) || + (tlen == 6 && strncmp (trim, ".data?", 6) == 0) || + (tlen == 4 && strncmp (trim, ".bss", 4) == 0)) { + + in_data = 1; + append_inline_text (&data, line, len); + + } else if ((tlen == 5 && strncmp (trim, ".code", 5) == 0) || + (tlen == 5 && strncmp (trim, ".text", 5) == 0)) { + in_data = 0; + } else if (in_data) { + append_inline_text (&data, line, len); + } else { + append_inline_text (&body, line, len); + } + + p = next; + + } + + free (*bodyp); + + *bodyp = body; + *datap = data; + +} + +static void remember_inline_function (const char *name, const char *asm_text, int return_label, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) { + + char *body; + char *data = 0; + + int i; + + if (!name || !*name || !asm_text) { + return; + } + + body = extract_inline_asm_body (asm_text, return_label); + + if (!body) { + return; + } + + split_inline_asm_sections (&body, &data); + i = find_inline_function (name); + + if (i < 0) { + + if (inline_function_count >= MAX_INLINE_FUNCTIONS) { + + free (body); + free (data); + + return; + + } + + i = inline_function_count++; + + inline_functions[i].name = xstrdup (name); + inline_functions[i].body = 0; + inline_functions[i].data = 0; + inline_functions[i].data_emitted = 0; + inline_functions[i].expanding = 0; + + } + + free (inline_functions[i].body); + free (inline_functions[i].data); + + inline_functions[i].body = body; + inline_functions[i].data = data; + inline_functions[i].data_emitted = 0; + inline_functions[i].param_count = param_count; + inline_functions[i].has_prototype = has_prototype ? 1 : 0; + inline_functions[i].returns_void = returns_void ? 1 : 0; + inline_functions[i].is_floating = is_floating ? 1 : 0; + inline_functions[i].return_size = return_size; + inline_functions[i].return_label = return_label; + inline_functions[i].expanding = 0; + inline_functions[i].usable = 1; + +} + +static void emit_inline_label_reference (int label, int return_label, int call_id) { + + if (label == return_label) { + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, "L%d", call_id); + } else { + fprintf (state->ofp, ".L%d", call_id); + } + + } else { + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, "L%d_%d", label, call_id); + } else { + fprintf (state->ofp, ".L%d_%d", label, call_id); + } + + } + +} + +static int emit_inline_parameter_reference_if_any (const char **pp, int argc, int stack_bytes) { + + const char *p = *pp; + const char *q; + + int offset = 0; + int param_index; + int stack_offset; + + if (!(state->syntax & ASM_SYNTAX_MASM)) { + return 0; + } + + if (*p != '[') { + return 0; + } + + q = p + 1; + + while (*q == ' ' || *q == '\t') { + q++; + } + + if (strncmp (q, "ebp", 3) != 0) { + return 0; + } + + q += 3; + + while (*q == ' ' || *q == '\t') { + q++; + } + + if (*q != '+') { + return 0; + } + + q++; + + while (*q == ' ' || *q == '\t') { + q++; + } + + if (*q < '0' || *q > '9') { + return 0; + } + + while (*q >= '0' && *q <= '9') { + + offset = (offset * 10) + (*q - '0'); + q++; + + } + + while (*q == ' ' || *q == '\t') { + q++; + } + + if (*q != ']') { + return 0; + } + + if (offset < 8 || ((offset - 8) % 4) != 0) { + return 0; + } + + param_index = (offset - 8) / 4; + + if (param_index < 0 || param_index >= argc) { + return 0; + } + + /* + * Inline arguments are copied into a compiler-owned temporary stack + * area before the inline body is emitted. Parameter 0 lives at the + * lowest address in that area, so [ebp + 8] maps to [esp], + * [ebp + 12] maps to [esp + 4], [ebp + 16] maps to [esp + 8], etc. + * If the inlined body changes ESP temporarily, stack_bytes keeps all + * parameter references pointed at the same argument copies. + */ + stack_offset = stack_bytes + (param_index * 4); + + if (stack_offset == 0) { + fprintf (state->ofp, "[esp]"); + } else if (stack_offset > 0) { + fprintf (state->ofp, "[esp + %d]", stack_offset); + } else { + fprintf (state->ofp, "[esp - %d]", -stack_offset); + } + + *pp = q + 1; + return 1; + +} + +static void emit_inline_line_substituted (const char *line, size_t len, int return_label, int call_id, int argc, int stack_bytes) { + + const char *p = line; + const char *end = line + len; + + while (p < end) { + + if ((state->syntax & ASM_SYNTAX_MASM) && *p == 'L' && p + 1 < end && p[1] >= '0' && p[1] <= '9') { + + const char *q = p + 1; + int label = 0; + + while (q < end && *q >= '0' && *q <= '9') { + + label = (label * 10) + (*q - '0'); + q++; + + } + + emit_inline_label_reference (label, return_label, call_id); + p = q; + + continue; + + } + + if (!(state->syntax & ASM_SYNTAX_MASM) && p + 2 < end && p[0] == '.' && p[1] == 'L' && p[2] >= '0' && p[2] <= '9') { + + const char *q = p + 2; + int label = 0; + + while (q < end && *q >= '0' && *q <= '9') { + + label = (label * 10) + (*q - '0'); + q++; + + } + + emit_inline_label_reference (label, return_label, call_id); + p = q; + + continue; + + } + + if (emit_inline_parameter_reference_if_any (&p, argc, stack_bytes)) { + continue; + } + + fputc (*p++, state->ofp); + + } + +} + +static int inline_line_stack_delta (const char *line, size_t len) { + + const char *p = line; + long n = 0; + int neg = 0; + + while (len > 0 && (*p == ' ' || *p == '\t')) { + + p++; + len--; + + } + + while (len > 0 && (p[len - 1] == '\r' || p[len - 1] == '\n' || p[len - 1] == ' ' || p[len - 1] == '\t')) { + len--; + } + + if (len >= 4 && strncmp (p, "push", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) { + return 4; + } + + if (len >= 3 && strncmp (p, "pop", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) { + return -4; + } + + if (len >= 8 && (strncmp (p, "sub esp,", 8) == 0 || strncmp (p, "add esp,", 8) == 0)) { + + neg = (p[0] == 'a'); + + p += 8; + len -= 8; + + while (len > 0 && (*p == ' ' || *p == '\t')) { + + p++; + len--; + + } + + while (len > 0 && *p >= '0' && *p <= '9') { + + n = (n * 10) + (*p - '0'); + + p++; + len--; + + } + + return neg ? -(int) n : (int) n; + + } + + if (len >= 10 && (strncmp (p, "subl $", 6) == 0 || strncmp (p, "addl $", 6) == 0)) { + + neg = (p[0] == 'a'); + + p += 6; + len -= 6; + + while (len > 0 && *p >= '0' && *p <= '9') { + + n = (n * 10) + (*p - '0'); + + p++; + len--; + + } + + while (len > 0 && (*p == ' ' || *p == '\t')) { + + p++; + len--; + + } + + if (len >= 6 && strncmp (p, ", %esp", 6) == 0) { + return neg ? -(int) n : (int) n; + } + + } + + return 0; + +} + +static int inline_body_stack_delta (const char *body) { + + const char *p = body; + int stack_bytes = 0; + + if (!body) { + return 0; + } + + while (*p) { + + const char *line = p; + const char *eol = strchr (p, '\n'); + const char *next = eol ? eol + 1 : p + strlen (p); + size_t len = (size_t) (next - line); + + stack_bytes += inline_line_stack_delta (line, len); + p = next; + + } + + return stack_bytes; + +} + +static int inline_parse_mov_eax_imm (const char *line, long *value) { + + const char *p = line; + + int neg = 0; + long v = 0; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov eax,", 8) != 0) { + return 0; + } + + p += 8; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p == '-') { + + neg = 1; + p++; + + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while (*p >= '0' && *p <= '9') { + + v = (v * 10) + (*p - '0'); + p++; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '\0') { + return 0; + } + + *value = neg ? -v : v; + return 1; + +} + +static int inline_parse_mov_edx_imm (const char *line, long *value) { + + const char *p = line; + + int neg = 0; + long v = 0; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov edx,", 8) != 0) { + return 0; + } + + p += 8; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p == '-') { + + neg = 1; + p++; + + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while (*p >= '0' && *p <= '9') { + + v = (v * 10) + (*p - '0'); + p++; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '\0') { + return 0; + } + + *value = neg ? -v : v; + return 1; + +} + +static int inline_line_is_exact (const char *line, const char *text) { + + const char *p = line; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, text, strlen (text)) != 0) { + return 0; + } + + p += strlen (text); + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + + +static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size); + +static unsigned long inline_u32 (long v) { + return ((unsigned long) v) & 0xffffffffUL; +} + +static long inline_s32 (long v) { + + unsigned long u = inline_u32 (v); + + if (u & 0x80000000UL) { + return (long) (u - 0x100000000UL); + } + + return (long) u; + +} + +static int inline_fold_binary_eax_edx (const char *line, long lhs, long rhs, long *out) { + + unsigned long ulhs; + unsigned long urhs; + unsigned long ures; + + if (!line || !out) { + return 0; + } + + ulhs = inline_u32 (lhs); + urhs = inline_u32 (rhs); + + if (inline_line_is_exact (line, "add eax, edx")) { + ures = ulhs + urhs; + } else if (inline_line_is_exact (line, "sub eax, edx")) { + ures = ulhs - urhs; + } else if (inline_line_is_exact (line, "imul eax, edx")) { + ures = (unsigned long) (inline_s32 (lhs) * inline_s32 (rhs)); + } else if (inline_line_is_exact (line, "and eax, edx")) { + ures = ulhs & urhs; + } else if (inline_line_is_exact (line, "or eax, edx")) { + ures = ulhs | urhs; + } else if (inline_line_is_exact (line, "xor eax, edx")) { + ures = ulhs ^ urhs; + } else { + return 0; + } + + *out = inline_s32 ((long) ures); + return 1; + +} + +static int inline_fold_shift_eax_edx (const char *line, long lhs, long rhs, long *out) { + + unsigned long ulhs; + unsigned long ures; + unsigned int count; + + long slhs; + + if (!line || !out) { + return 0; + } + + count = (unsigned int) (inline_u32 (rhs) & 31UL); + ulhs = inline_u32 (lhs); + + if (inline_line_is_exact (line, "shl eax, cl")) { + ures = ulhs << count; + } else if (inline_line_is_exact (line, "sal eax, cl")) { + ures = ulhs << count; + } else if (inline_line_is_exact (line, "sar eax, cl")) { + + slhs = inline_s32 (lhs); + ures = inline_u32 (slhs >> count); + + } else if (inline_line_is_exact (line, "shr eax, cl")) { + ures = ulhs >> count; + } else { + return 0; + } + + *out = inline_s32 ((long) ures); + return 1; + +} + +static int inline_fold_div_eax_edx (long lhs, long rhs, long *out) { + + long slhs; + long srhs; + + if (!out) { + return 0; + } + + srhs = inline_s32 (rhs); + + if (srhs == 0) { + return 0; + } + + slhs = inline_s32 (lhs); + + *out = inline_s32 (slhs / srhs); + return 1; + +} + +static int inline_fold_mod_eax_edx (long lhs, long rhs, long *out) { + + long slhs; + long srhs; + + if (!out) { + return 0; + } + + srhs = inline_s32 (rhs); + + if (srhs == 0) { + return 0; + } + + slhs = inline_s32 (lhs); + + *out = inline_s32 (slhs % srhs); + return 1; + +} + +static int inline_parse_cond_jump_label (const char *line, char *op, size_t op_size, char *label, size_t label_size) { + + static const char *const ops[] = { + "jz", "jnz", "je", "jne", "ja", "jae", "jb", "jbe", + "jg", "jge", "jl", "jle", "js", "jns", 0 + }; + + int i; + + if (!op || op_size == 0) { + return 0; + } + + for (i = 0; ops[i]; i++) { + + if (inline_parse_jump_label (line, ops[i], label, label_size)) { + + strncpy (op, ops[i], op_size - 1); + op[op_size - 1] = '\0'; + + return 1; + + } + + } + + return 0; + +} + +static int inline_eval_test_jump (const char *op, long value) { + + long svalue = inline_s32 (value); + + if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) { + return svalue == 0; + } + + if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) { + return svalue != 0; + } + + if (strcmp (op, "js") == 0) { + return svalue < 0; + } + + if (strcmp (op, "jns") == 0) { + return svalue >= 0; + } + + return 0; + +} + +static int inline_eval_cmp_jump (const char *op, long lhs, long rhs) { + + long slhs = inline_s32 (lhs); + long srhs = inline_s32 (rhs); + + unsigned long ulhs = inline_u32 (lhs); + unsigned long urhs = inline_u32 (rhs); + + if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) { + return slhs == srhs; + } + + if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) { + return slhs != srhs; + } + + if (strcmp (op, "jg") == 0) { + return slhs > srhs; + } + + if (strcmp (op, "jge") == 0) { + return slhs >= srhs; + } + + if (strcmp (op, "jl") == 0) { + return slhs < srhs; + } + + if (strcmp (op, "jle") == 0) { + return slhs <= srhs; + } + + if (strcmp (op, "ja") == 0) { + return ulhs > urhs; + } + + if (strcmp (op, "jae") == 0) { + return ulhs >= urhs; + } + + if (strcmp (op, "jb") == 0) { + return ulhs < urhs; + } + + if (strcmp (op, "jbe") == 0) { + return ulhs <= urhs; + } + + return 0; + +} + +static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size) { + + const char *p = line; + size_t n = 0; + + if (!label || label_size == 0) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, op, strlen (op)) != 0) { + return 0; + } + + p += strlen (op); + + if (*p != ' ' && *p != '\t') { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) { + return 0; + } + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') { + + if (n + 1 < label_size) { + label[n++] = *p; + } + + p++; + } + + label[n] = '\0'; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + + + +static int inline_parse_any_jump_label (const char *line, char *label, size_t label_size) { + + static const char *const ops[] = { + "jmp", "jz", "jnz", "je", "jne", "jc", "jnc", + "ja", "jae", "jb", "jbe", "jg", "jge", "jl", "jle", + "js", "jns", "jo", "jno", "jp", "jpe", "jnp", "jpo", + 0 + }; + + int i; + + for (i = 0; ops[i]; i++) { + + if (inline_parse_jump_label (line, ops[i], label, label_size)) { + return 1; + } + + } + + return 0; + +} + +static int inline_parse_label_definition (const char *line, char *label, size_t label_size) { + + const char *p = line; + size_t n = 0; + + if (!label || label_size == 0) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) { + return 0; + } + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') { + + if (n + 1 < label_size) { + label[n++] = *p; + } + + p++; + } + + label[n] = '\0'; + + if (*p != ':') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + +static const char *inline_find_chain_target (char **from, char **to, int count, const char *label) { + + int i; + + if (!label) { + return 0; + } + + for (i = 0; i < count; i++) { + + if (from[i] && to[i] && strcmp (from[i], label) == 0) { + return to[i]; + } + + } + + return 0; + +} + +static void inline_copy_string (char *dst, const char *src, size_t dst_size) { + + size_t len; + + if (!dst || dst_size == 0) { + return; + } + + if (!src) { + + dst[0] = '\0'; + return; + + } + + len = strlen (src); + + if (len >= dst_size) { + len = dst_size - 1; + } + + memcpy (dst, src, len); + dst[len] = '\0'; + +} + +static int inline_resolve_jump_chain (char **from, char **to, int count, const char *label, char *out, size_t out_size) { + + const char *cur = label; + const char *next; + + int changed = 0; + int depth = 0; + + if (!label || !out || out_size == 0) { + return 0; + } + + while (depth++ < count) { + + next = inline_find_chain_target (from, to, count, cur); + + if (!next || strcmp (next, cur) == 0) { + break; + } + + cur = next; + changed = 1; + + } + + if (!changed || strcmp (cur, label) == 0) { + return 0; + } + + if (out && cur && out_size > 0) { + inline_copy_string (out, cur, out_size); + } + + return 1; + +} + +static int inline_parse_esp_reference (const char *p, int *offset) { + + const char *start = p; + + int off = 0; + int neg = 0; + + if (strncmp (p, "[esp]", 5) == 0) { + + *offset = 0; + return 5; + + } + + if (strncmp (p, "[esp + ", 7) == 0) { + p += 7; + } else if (strncmp (p, "[esp+", 5) == 0) { + p += 5; + } else if (strncmp (p, "[esp - ", 7) == 0) { + neg = 1; + p += 7; + } else if (strncmp (p, "[esp-", 5) == 0) { + neg = 1; + p += 5; + } else { + return 0; + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while (*p >= '0' && *p <= '9') { + + off = (off * 10) + (*p - '0'); + p++; + + } + + if (*p != ']') { + return 0; + } + + *offset = neg ? -off : off; + return (int) ((p + 1) - start); + +} + +static int inline_parse_store_eax_to_esp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov dword ptr ", 14) != 0) { + return 0; + } + + p += 14; + n = inline_parse_esp_reference (p, offset); + + if (!n) { + return 0; + } + + p += n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, ", eax", 5) != 0) { + return 0; + } + + p += 5; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + +static int inline_parse_load_eax_from_esp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov eax, dword ptr ", 19) != 0) { + return 0; + } + + p += 19; + n = inline_parse_esp_reference (p, offset); + + if (!n) { + return 0; + } + + p += n; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + +static int inline_parse_load_edx_from_esp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov edx, dword ptr ", 19) != 0) { + return 0; + } + + p += 19; + n = inline_parse_esp_reference (p, offset); + + if (!n) { + return 0; + } + + p += n; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + +static int inline_parse_addsub_esp_imm1 (const char *line, int *offset, long *delta) { + + const char *p = line; + int n; + int is_sub = 0; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "add dword ptr ", 14) == 0) { + p += 14; + } else if (strncmp (p, "sub dword ptr ", 14) == 0) { + + is_sub = 1; + p += 14; + + } else { + return 0; + } + + n = inline_parse_esp_reference (p, offset); + + if (!n) { + return 0; + } + + p += n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, ", 1", 3) != 0) { + return 0; + } + + p += 3; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '\0') { + return 0; + } + + *delta = is_sub ? -1 : 1; + return 1; + +} + +static char *inline_dup_text_slice (const char *start, size_t len) { + + char *out = (char *) malloc (len + 1); + + if (!out) { + return 0; + } + + memcpy (out, start, len); + out[len] = '\0'; + + return out; + +} + +static void inline_clear_slots (int *slot_valid, long *slot_value) { + + int i; + + for (i = 0; i < 64; i++) { + + slot_valid[i] = 0; + slot_value[i] = 0; + + } + +} + +static void emit_inline_optimized_text (const char *text, FILE *ofp) { + + const char *p = text; + + char **lines; + char **repl; + + int *skip; + int *store_slot; + int count = 0; + int i; + + long slot_value[64]; + + int slot_valid[64]; + int slot_real_read[64]; + + long eax_value = 0; + + int eax_valid = 0; + int alloc_line = -1; + int free_line = -1; + int alloc_bytes = 0; + int can_remove_area = 0; + + char **chain_from = 0; + char **chain_to = 0; + + int *label_refs = 0; + + if (!text || !ofp) { + return; + } + + for (p = text; *p; p++) { + + if (*p == '\n') { + count++; + } + + } + + if (p != text && p[-1] != '\n') { + count++; + } + + if (count <= 0) { + return; + } + + lines = (char **) calloc ((size_t) count, sizeof (*lines)); + repl = (char **) calloc ((size_t) count, sizeof (*repl)); + skip = (int *) calloc ((size_t) count, sizeof (*skip)); + store_slot = (int *) malloc ((size_t) count * sizeof (*store_slot)); + chain_from = (char **) calloc ((size_t) count, sizeof (*chain_from)); + chain_to = (char **) calloc ((size_t) count, sizeof (*chain_to)); + label_refs = (int *) calloc ((size_t) count, sizeof (*label_refs)); + + if (!lines || !repl || !skip || !store_slot || !chain_from || !chain_to || !label_refs) { + + free (lines); + free (repl); + free (skip); + free (store_slot); + free (chain_from); + free (chain_to); + free (label_refs); + + fputs (text, ofp); + return; + + } + + for (i = 0; i < count; i++) { + store_slot[i] = -1; + } + + p = text; + i = 0; + + while (*p && i < count) { + + const char *line = p; + const char *eol = strchr (p, '\n'); + const char *next = eol ? eol + 1 : p + strlen (p); + size_t len = (size_t) (next - line); + + lines[i++] = inline_dup_text_slice (line, len); + p = next; + + } + + count = i; + inline_clear_slots (slot_valid, slot_value); + + for (i = 0; i < 64; i++) { + slot_real_read[i] = 0; + } + + for (i = 0; i < count; i++) { + + char *buf = lines[i]; + int offset; + int slot; + long value; + long mem_delta; + int delta; + + if (!buf) { + continue; + } + + delta = inline_line_stack_delta (buf, strlen (buf)); + + if (delta > 0 && alloc_line < 0) { + + alloc_line = i; + alloc_bytes = delta; + + eax_valid = 0; + continue; + + } + + if (delta < 0 && alloc_line >= 0 && -delta == alloc_bytes) { + + free_line = i; + eax_valid = 0; + + continue; + + } + + if (inline_parse_mov_eax_imm (buf, &value)) { + + eax_value = value; + eax_valid = 1; + + continue; + + } + + if (inline_parse_store_eax_to_esp (buf, &offset)) { + + if (offset >= 0 && (offset % 4) == 0) { + + slot = offset / 4; + + if (slot >= 0 && slot < 64) { + + store_slot[i] = slot; + + if (eax_valid) { + + slot_valid[slot] = 1; + slot_value[slot] = eax_value; + + } else { + slot_valid[slot] = 0; + } + + } + + } + + continue; + + } + + if (inline_parse_load_eax_from_esp (buf, &offset)) { + + if (offset >= 0 && (offset % 4) == 0) { + + slot = offset / 4; + + if (slot >= 0 && slot < 64 && slot_valid[slot]) { + + char tmp[128]; + sprintf (tmp, " mov eax, %ld\n", slot_value[slot]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + eax_value = slot_value[slot]; + eax_valid = 1; + + continue; + + } + + if (slot >= 0 && slot < 64) { + slot_real_read[slot] = 1; + } + + } + + eax_valid = 0; + continue; + } + + if (inline_parse_load_edx_from_esp (buf, &offset)) { + + if (offset >= 0 && (offset % 4) == 0) { + + slot = offset / 4; + + if (slot >= 0 && slot < 64 && slot_valid[slot]) { + + char tmp[128]; + sprintf (tmp, " mov edx, %ld\n", slot_value[slot]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + continue; + + } + + if (slot >= 0 && slot < 64) { + slot_real_read[slot] = 1; + } + + } + + continue; + + } + + if (inline_parse_addsub_esp_imm1 (buf, &offset, &mem_delta)) { + + if (offset >= 0 && (offset % 4) == 0) { + + slot = offset / 4; + + if (slot >= 0 && slot < 64) { + + if (slot_valid[slot]) { + slot_value[slot] = inline_s32 (slot_value[slot] + mem_delta); + skip[i] = 1; + } + + continue; + } + } + + eax_valid = 0; + continue; + + } + + if (strstr (buf, "esp")) { + + inline_clear_slots (slot_valid, slot_value); + eax_valid = 0; + + } else if (strstr (buf, "eax")) { + eax_valid = 0; + } + + } + + if (alloc_line >= 0 && free_line > alloc_line && alloc_bytes > 0) { + + int any_remaining_esp = 0; + int slots = alloc_bytes / 4; + + for (i = 0; i < count; i++) { + + if (i == alloc_line || i == free_line) { + continue; + } + + if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) { + continue; + } + + if (repl[i]) { + + if (strstr (repl[i], "esp")) { + any_remaining_esp = 1; + break; + } + + } else if (lines[i] && strstr (lines[i], "esp")) { + + any_remaining_esp = 1; + break; + + } + + } + + if (!any_remaining_esp) { + + can_remove_area = 1; + skip[alloc_line] = 1; + skip[free_line] = 1; + + for (i = 0; i < count; i++) { + + if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) { + skip[i] = 1; + } + + } + + /* + * If an argument copy was folded away, the immediately preceding + * constant load was only there to feed that copy. Drop it too; + * otherwise multi-argument inline calls leave noise like: + * + * mov eax, 1 + * mov eax, 2 + * + * after both temporary argument stores have been removed. + */ + for (i = 0; i < count; i++) { + + long ignored_value; + int j; + + if (!skip[i] || store_slot[i] < 0) { + continue; + } + + for (j = i - 1; j >= 0; j--) { + + if (skip[j]) { + continue; + } + + if (inline_parse_mov_eax_imm (lines[j], &ignored_value)) { + skip[j] = 1; + } + + break; + + } + + } + + } + + } + + (void) can_remove_area; + + for (i = 0; i < count; i++) { + + long first_value; + int j; + + if (skip[i] || !lines[i] || !inline_parse_mov_eax_imm (lines[i], &first_value)) { + continue; + } + + for (j = i + 1; j < count; j++) { + + long next_value; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (!check) { + continue; + } + + if (!strstr (check, "eax")) { + continue; + } + + if (inline_parse_mov_eax_imm (check, &next_value) && next_value == first_value) { + skip[i] = 1; + } + + break; + + } + + } + + for (i = 0; i + 3 < count; i++) { + + long lhs; + long rhs; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + + if (!l0 || !l1 || !l2 || !l3) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, lhs, rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + + } + + } + + for (i = 0; i + 4 < count; i++) { + + long lhs; + long rhs; + + char false_label[128]; + char true_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + + if (!l0 || !l1 || !l2 || !l3 || !l4) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) { + continue; + } + + if (!inline_parse_jump_label (l4, "jmp", true_label, sizeof (true_label))) { + continue; + } + + sprintf (tmp, " jmp %s\n", inline_eval_cmp_jump (opbuf, lhs, rhs) ? false_label : true_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + + } + + for (i = 0; i + 4 < count; i++) { + + long lhs; + long rhs; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + + if (!l0 || !l1 || !l2 || !l3 || !l4) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) { + continue; + } + + if (!inline_line_is_exact (l3, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_test_jump (opbuf, lhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + + } + + } + + for (i = 0; i + 5 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l4, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } + + } + + for (i = 0; i + 5 < count; i++) { + + long lhs; + long rhs; + + char false_label[128]; + char true_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) { + continue; + } + + if (!inline_line_is_exact (l3, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) { + continue; + } + + if (!inline_parse_jump_label (l5, "jmp", true_label, sizeof (true_label))) { + continue; + } + + sprintf (tmp, " jmp %s\n", inline_eval_test_jump (opbuf, lhs) ? false_label : true_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } + + for (i = 0; i + 6 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + int jump_taken = 0; + + char cond_label[128]; + char fall_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l4, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (!inline_parse_jump_label (l6, "jmp", fall_label, sizeof (fall_label))) { + continue; + } + + sprintf (tmp, " jmp %s\n", jump_taken ? cond_label : fall_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } + + for (i = 0; i + 5 < count; i++) { + + long lhs; + long rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx")) { + continue; + } + + if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l4, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_test_jump (opbuf, folded); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } + + } + + for (i = 0; i + 6 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx")) { + continue; + } + + if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l5, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } + + } + + for (i = 0; i + 6 < count; i++) { + + long lhs; + long rhs; + long folded; + + char false_label[128]; + char true_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx")) { + continue; + } + + if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l4, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) { + continue; + } + + if (!inline_parse_jump_label (l6, "jmp", true_label, sizeof (true_label))) { + continue; + } + + sprintf (tmp, " jmp %s\n", inline_eval_test_jump (opbuf, folded) ? false_label : true_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } + + for (i = 0; i + 7 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + int jump_taken = 0; + + char cond_label[128]; + char fall_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + const char *l7; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx")) { + continue; + } + + if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l5, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (!inline_parse_jump_label (l7, "jmp", fall_label, sizeof (fall_label))) { + continue; + } + + sprintf (tmp, " jmp %s\n", jump_taken ? cond_label : fall_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + + } + + for (i = 0; i + 6 < count; i++) { + + long lhs; + long rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx")) { + continue; + } + + if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l5, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_test_jump (opbuf, folded); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + + } + + } + + for (i = 0; i + 7 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + const char *l7; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx")) { + continue; + } + + if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l5, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l6, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + + } + + } + + for (i = 0; i + 7 < count; i++) { + + long lhs; + long rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + const char *l7; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) { + continue; + } + + if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l6, "test eax, eax")) { + continue; + } + + if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_test_jump (opbuf, folded); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + + } + + } + + for (i = 0; i + 8 < count; i++) { + + long lhs; + long rhs; + long cmp_rhs; + long folded; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + const char *l6; + const char *l7; + const char *l8; + + int taken; + + if (skip[i]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6]; + l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7]; + l8 = repl[i + 8] ? repl[i + 8] : lines[i + 8]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7 || !l8) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) { + continue; + } + + if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l6, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l7, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l8, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + skip[i + 8] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + skip[i + 6] = 1; + skip[i + 7] = 1; + skip[i + 8] = 1; + + } + + } + + for (i = 0; i + 2 < count; i++) { + + long lhs; + long rhs; + long folded; + + char tmp[128]; + + const char *l0; + const char *l1; + const char *l2; + + if (skip[i] || skip[i + 1] || skip[i + 2]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + + if (!l0 || !l1 || !l2) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) { + continue; + } + + sprintf (tmp, " mov eax, %ld\n", folded); + free (repl[i]); + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + + } + + for (i = 0; i + 3 < count; i++) { + + long lhs; + long rhs; + long folded; + + char tmp[128]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + + if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + + if (!l0 || !l1 || !l2 || !l3) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx")) { + continue; + } + + if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) { + continue; + } + + sprintf (tmp, " mov eax, %ld\n", folded); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + + } + + for (i = 0; i + 4 < count; i++) { + + long lhs; + long rhs; + long folded; + + char tmp[128]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + + if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + + if (!l0 || !l1 || !l2 || !l3 || !l4) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx")) { + continue; + } + + if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) { + continue; + } + + sprintf (tmp, " mov eax, %ld\n", folded); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + + } + + for (i = 0; i + 5 < count; i++) { + + long lhs; + long rhs; + long folded; + + char tmp[128]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + const char *l4; + const char *l5; + + if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4] || skip[i + 5]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4]; + l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5]; + + if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) { + continue; + } + + if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) { + continue; + } + + sprintf (tmp, " mov eax, %ld\n", folded); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + skip[i + 4] = 1; + skip[i + 5] = 1; + + } + + /* + * Some compound-expression folds, such as x += 1, are reduced only by + * the late arithmetic pass above. Run the constant compare/branch fold + * again here so newly-created sequences like: + * + * mov eax, 9 + * mov edx, 9 + * cmp eax, edx + * jne Lx + * + * are folded after the arithmetic result becomes visible. + */ + for (i = 0; i + 3 < count; i++) { + + long lhs; + long rhs; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + + int taken; + + if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) { + continue; + } + + l0 = repl[i] ? repl[i] : lines[i]; + l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1]; + l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2]; + l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3]; + + if (!l0 || !l1 || !l2 || !l3) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, lhs, rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + + } else { + + skip[i] = 1; + skip[i + 1] = 1; + skip[i + 2] = 1; + skip[i + 3] = 1; + + } + + } + + /* + * A late fold can leave useful instructions separated by skipped source + * lines. For example x += 1 followed by == may become: + * + * mov eax, 9 + * ; skipped old rhs/op lines + * mov edx, 9 + * cmp eax, edx + * jne Lx + * + * The normal adjacent compare fold cannot see through the skipped lines, + * so do one compacting pass over the remaining live instructions. + */ + for (i = 0; i < count; i++) { + + int j0; + int j1; + int j2; + int j3; + + long lhs; + long rhs; + + char cond_label[128]; + char opbuf[16]; + char tmp[192]; + + const char *l0; + const char *l1; + const char *l2; + const char *l3; + + int taken; + + if (skip[i]) { + continue; + } + + j0 = i; + j1 = j0 + 1; + + while (j1 < count && skip[j1]) { + j1++; + } + + j2 = j1 + 1; + + while (j2 < count && skip[j2]) { + j2++; + } + + j3 = j2 + 1; + + while (j3 < count && skip[j3]) { + j3++; + } + + if (j1 >= count || j2 >= count || j3 >= count) { + continue; + } + + l0 = repl[j0] ? repl[j0] : lines[j0]; + l1 = repl[j1] ? repl[j1] : lines[j1]; + l2 = repl[j2] ? repl[j2] : lines[j2]; + l3 = repl[j3] ? repl[j3] : lines[j3]; + + if (!l0 || !l1 || !l2 || !l3) { + continue; + } + + if (!inline_parse_mov_eax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_edx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp eax, edx")) { + continue; + } + + if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) { + continue; + } + + taken = inline_eval_cmp_jump (opbuf, lhs, rhs); + + if (taken) { + + sprintf (tmp, " jmp %s\n", cond_label); + free (repl[j0]); + + repl[j0] = inline_dup_text_slice (tmp, strlen (tmp)); + + skip[j1] = 1; + skip[j2] = 1; + skip[j3] = 1; + + } else { + + skip[j0] = 1; + skip[j1] = 1; + skip[j2] = 1; + skip[j3] = 1; + + } + + } + + for (i = 0; i < count; i++) { + + char label[128]; + char target[128]; + const char *line; + int j; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_label_definition (line, label, sizeof (label))) { + continue; + } + + for (j = i + 1; j < count; j++) { + + const char *next_line; + + if (skip[j]) { + continue; + } + + next_line = repl[j] ? repl[j] : lines[j]; + + if (!next_line) { + continue; + } + + if (inline_parse_jump_label (next_line, "jmp", target, sizeof (target))) { + + if (strcmp (label, target) != 0) { + + chain_from[i] = inline_dup_text_slice (label, strlen (label)); + chain_to[i] = inline_dup_text_slice (target, strlen (target)); + + } + + } + + break; + + } + + } + + for (i = 0; i < count; i++) { + + char target[128]; + char resolved[128]; + char tmp[192]; + const char *line; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) { + continue; + } + + if (!inline_resolve_jump_chain (chain_from, chain_to, count, target, resolved, sizeof (resolved))) { + continue; + } + + sprintf (tmp, " jmp %s\n", resolved); + free (repl[i]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + } + + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) { + continue; + } + + for (j = 0; j < count; j++) { + + char label[128]; + const char *def; + + if (skip[j]) { + continue; + } + + def = repl[j] ? repl[j] : lines[j]; + + if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) { + + label_refs[j]++; + break; + + } + + } + + } + + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + int target_line = -1; + int can_delete = 1; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) { + continue; + } + + for (j = i + 1; j < count; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) { + + target_line = j; + break; + + } + + } + + if (target_line < 0) { + continue; + } + + for (j = i + 1; j < target_line; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) { + + can_delete = 0; + break; + + } + + } + + if (!can_delete) { + continue; + } + + for (j = i + 1; j < target_line; j++) { + skip[j] = 1; + } + + } + + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + int target_line = -1; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) { + continue; + } + + for (j = i + 1; j < count; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (!check) { + continue; + } + + if (inline_parse_label_definition (check, label, sizeof (label))) { + + if (strcmp (label, target) == 0) { + target_line = j; + } + + break; + + } + + break; + + } + + if (target_line >= 0) { + skip[i] = 1; + } + + } + + /* + * Branch folding can create new unreachable ranges after the earlier + * jump cleanup has already run. Recompute label references from the + * current non-skipped text and remove dead fall-through ranges between + * an unconditional jump and its later target. This deliberately keeps + * referenced labels, so externally reachable or still-jumped-to blocks + * are not discarded. + */ + { + + int changed; + int pass; + + for (pass = 0; pass < count; pass++) { + + changed = 0; + + for (i = 0; i < count; i++) { + label_refs[i] = 0; + } + + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) { + continue; + } + + for (j = 0; j < count; j++) { + + char label[128]; + const char *def; + + if (skip[j]) { + continue; + } + + def = repl[j] ? repl[j] : lines[j]; + + if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) { + + label_refs[j]++; + break; + + } + + } + + } + + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + int target_line = -1; + int can_delete = 1; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) { + continue; + } + + for (j = i + 1; j < count; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) { + + target_line = j; + break; + + } + + } + + if (target_line < 0) { + continue; + } + + for (j = i + 1; j < target_line; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) { + + can_delete = 0; + break; + + } + + } + + if (!can_delete) { + continue; + } + + for (j = i + 1; j < target_line; j++) { + + if (!skip[j]) { + + skip[j] = 1; + changed = 1; + + } + + } + + } + + if (!changed) { + break; + } + + } + + } + + /* + * The second unreachable-code pass can expose a new fall-through jump, + * for example "jmp L9" immediately followed by "L9:". Run this small + * cleanup again at the end so later branch/unreachable optimisations do + * not leave the already-handled form behind. + */ + for (i = 0; i < count; i++) { + + char target[128]; + const char *line; + int j; + int target_line = -1; + + if (skip[i]) { + continue; + } + + line = repl[i] ? repl[i] : lines[i]; + + if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) { + continue; + } + + for (j = i + 1; j < count; j++) { + + char label[128]; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (!check) { + continue; + } + + if (inline_parse_label_definition (check, label, sizeof (label))) { + + if (strcmp (label, target) == 0) { + target_line = j; + } + + break; + + } + + break; + + } + + if (target_line >= 0) { + skip[i] = 1; + } + + } + + for (i = 0; i < count; i++) { + + if (!skip[i]) { + fputs (repl[i] ? repl[i] : (lines[i] ? lines[i] : ""), ofp); + } + + free (lines[i]); + free (repl[i]); + + } + + for (i = 0; i < count; i++) { + + free (chain_from[i]); + free (chain_to[i]); + + } + + free (lines); + free (repl); + free (skip); + free (store_slot); + free (chain_from); + free (chain_to); + free (label_refs); + +} + +static void finish_inline_buffer (FILE **tmp, FILE **saved, int optimize) { + + FILE *out; + long size; + char *buf; + + if (!tmp || !saved || !*tmp || !*saved) { + return; + } + + out = *saved; + + fflush (*tmp); + fseek (*tmp, 0, SEEK_END); + + size = ftell (*tmp); + fseek (*tmp, 0, SEEK_SET); + + if (size > 0) { + + buf = (char *) malloc ((size_t) size + 1); + + if (buf) { + + if (fread (buf, 1, (size_t) size, *tmp) == (size_t) size) { + + buf[size] = '\0'; + + if (optimize) { + emit_inline_optimized_text (buf, out); + } else { + fwrite (buf, 1, (size_t) size, out); + } + + } + + free (buf); + + } + + } + + fclose (*tmp); + + *tmp = 0; + *saved = 0; + + state->ofp = out; + +} + +static void emit_inline_body_substituted (const char *body, int return_label, int argc) { + + const char *p = body; + + int call_id = anon_label++; + int stack_bytes = 0; + + if (!body || !state->ofp) { + return; + } + + while (*p) { + + const char *line = p; + const char *eol = strchr (p, '\n'); + const char *next = eol ? eol + 1 : p + strlen (p); + size_t len = (size_t) (next - line); + + emit_inline_line_substituted (line, len, return_label, call_id, argc, stack_bytes); + stack_bytes += inline_line_stack_delta (line, len); + p = next; + + } + + if (body[0] && body[strlen (body) - 1] != '\n') { + fputc ('\n', state->ofp); + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, "L%d:\n", call_id); + } else { + fprintf (state->ofp, ".L%d:\n", call_id); + } + +} + +static int emit_inline_call_if_possible (const char *name, int argc, const char *reg) { + + int i = find_inline_function (name); + + if (i < 0 || !inline_functions[i].usable) { + return 0; + } + + if (argc != inline_functions[i].param_count) { + return 0; + } + + if (inline_functions[i].is_floating) { + return 0; + } + + if (inline_functions[i].expanding) { + return 0; + } + + if (!state->ofp) { + return 1; + } + + if (!inline_functions[i].body && !inline_functions[i].returns_void) { + return 0; + } + + if (inline_functions[i].body && inline_body_stack_delta (inline_functions[i].body) != 0) { + return 0; + } + + if (inline_functions[i].data && !inline_functions[i].data_emitted) { + + fputs (inline_functions[i].data, state->ofp); + + if (inline_functions[i].data[0] && inline_functions[i].data[strlen (inline_functions[i].data) - 1] != '\n') { + fputc ('\n', state->ofp); + } + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, ".code\n"); + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "section .text\n"); + } else { + fprintf (state->ofp, ".text\n"); + } + + inline_functions[i].data_emitted = 1; + + } + + if (inline_functions[i].body) { + + inline_functions[i].expanding = 1; + emit_inline_body_substituted (inline_functions[i].body, inline_functions[i].return_label, argc); + inline_functions[i].expanding = 0; + + } + + if (!inline_functions[i].returns_void && strcmp (reg, "eax") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } else { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + return 1; + +} + +#define MAX_LOCAL_SYMBOLS 1024 + +struct local_symbol { + + char *name; + + int size; + int align; + + int is_floating; + long offset; + + char *static_label; + + int is_static, is_unsigned; + int is_array; + int array_element_size; + + int pointer_depth; + int pointed_size; + + char *pointed_tag_name; + char *tag_name; + +}; + +static struct local_symbol local_symbols[MAX_LOCAL_SYMBOLS]; + +static int local_array_pointer_step_size (const struct local_symbol *sym); +static int global_array_pointer_step_size (const char *name); + +static int local_symbol_count = 0; + +static long current_local_stack_size = 0; +static long current_block_cleanup_bytes = 0; + +static long align_up_long (long value, int align) { + + long mask; + + if (align <= 1) { + return value; + } + + mask = (long) align - 1; + return (value + mask) & ~mask; + +} + +static int type_alignment (int size) { + + if (size <= 1) { + return 1; + } + + if (size == 2) { + return 2; + } + + return 4; + +} + +static void reset_local_symbols (void) { + + int i; + + for (i = 0; i < local_symbol_count; i++) { + + if (local_symbols[i].name) { + + free (local_symbols[i].name); + local_symbols[i].name = 0; + + } + + if (local_symbols[i].static_label) { + + free (local_symbols[i].static_label); + local_symbols[i].static_label = 0; + + } + + if (local_symbols[i].pointed_tag_name) { + + free (local_symbols[i].pointed_tag_name); + local_symbols[i].pointed_tag_name = 0; + + } + + if (local_symbols[i].tag_name) { + + free (local_symbols[i].tag_name); + local_symbols[i].tag_name = 0; + + } + + } + + local_symbol_count = 0; + current_local_stack_size = 0; + +} + +static void truncate_local_symbols (int count, long stack_size) { + + int i; + + if (count < 0) { + count = 0; + } + + for (i = count; i < local_symbol_count; i++) { + + if (local_symbols[i].name) { + + free (local_symbols[i].name); + local_symbols[i].name = 0; + + } + + if (local_symbols[i].static_label) { + + free (local_symbols[i].static_label); + local_symbols[i].static_label = 0; + + } + + if (local_symbols[i].pointed_tag_name) { + + free (local_symbols[i].pointed_tag_name); + local_symbols[i].pointed_tag_name = 0; + + } + + if (local_symbols[i].tag_name) { + + free (local_symbols[i].tag_name); + local_symbols[i].tag_name = 0; + + } + + } + + local_symbol_count = count; + current_local_stack_size = stack_size; + +} + +static int local_symbol_exists_in_current_scope (const char *name, int start_count) { + + int i; + + if (!name) { + return 0; + } + + for (i = start_count; i < local_symbol_count; i++) { + + if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) { + return 1; + } + + } + + return 0; + +} + +static long add_local_symbol (const char *name, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) { + + long new_size; + + if (!name) { + return 0; + } + + if (size < 1) { + size = 1; + } + + if (align < 1) { + align = 1; + } + + if (local_symbol_exists_in_current_scope (name, scope_start_count)) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name); + return 0; + + } + + if (local_symbol_count >= MAX_LOCAL_SYMBOLS) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many local symbols"); + return 0; + + } + + new_size = align_up_long (current_local_stack_size + size, align); + + /* + * EBP-relative locals must be backed by whole stack slots. Without this, + * a byte-sized local following two pointer locals can be assigned offset + * -9(%ebp), while the emitted stack adjustment has only reserved 8 bytes. + * That is exactly what happened in cpplib/lex.c's _cpp_skip_block_comment: + * + * cpp_mffc *mffc; -> -4 + * const char *pos; -> -8 + * char c; -> -9, outside the reserved frame + * + * Keep the object's real size for loads/stores, but round the frame growth + * so subsequent stack allocation reserves enough bytes before the local is + * used. + */ + new_size = align_up_long (new_size, 4); + + local_symbols[local_symbol_count].name = xstrdup (name); + local_symbols[local_symbol_count].size = size; + local_symbols[local_symbol_count].align = align; + local_symbols[local_symbol_count].offset = -(int) new_size; + local_symbols[local_symbol_count].is_static = 0; + local_symbols[local_symbol_count].static_label = 0; + local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0; + local_symbols[local_symbol_count].is_floating = 0; + local_symbols[local_symbol_count].is_array = 0; + local_symbols[local_symbol_count].array_element_size = 0; + local_symbols[local_symbol_count].pointer_depth = 0; + local_symbols[local_symbol_count].pointed_size = 0; + local_symbols[local_symbol_count].pointed_tag_name = 0; + local_symbols[local_symbol_count].tag_name = 0; + + local_symbol_count++; + current_local_stack_size = new_size; + + return -(long) new_size; + +} + +static void add_static_local_symbol (const char *name, const char *label, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) { + + if (!name || !label) { + return; + } + + if (size < 1) { + size = 1; + } + + if (align < 1) { + align = 1; + } + + if (local_symbol_exists_in_current_scope (name, scope_start_count)) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name); + return; + + } + + if (local_symbol_count >= MAX_LOCAL_SYMBOLS) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many local symbols"); + return; + + } + + local_symbols[local_symbol_count].name = xstrdup (name); + local_symbols[local_symbol_count].static_label = xstrdup (label); + local_symbols[local_symbol_count].size = size; + local_symbols[local_symbol_count].align = align; + local_symbols[local_symbol_count].offset = 0; + local_symbols[local_symbol_count].is_static = 1; + local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0; + local_symbols[local_symbol_count].is_floating = 0; + local_symbols[local_symbol_count].is_array = 0; + local_symbols[local_symbol_count].array_element_size = 0; + + local_symbol_count++; + +} + +static struct local_symbol *find_local_symbol (const char *name); + +static void set_local_symbol_array (const char *name, int is_array) { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym) { + sym->is_array = is_array ? 1 : 0; + } + +} + +static void set_local_symbol_array_element_size (const char *name, int elem_size) { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym) { + sym->array_element_size = elem_size > 0 ? elem_size : 0; + } + +} + +static void set_local_symbol_floating (const char *name, int is_floating) { + + int i; + + if (!name) { + return; + } + + for (i = local_symbol_count - 1; i >= 0; i--) { + + if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) { + + local_symbols[i].is_floating = is_floating ? 1 : 0; + return; + + } + + } + +} + +#define MAX_PENDING_PARAMS 128 + +struct pending_param { + + char *name; + + int align, size; + int is_unsigned; + int is_floating; + int pointer_depth; + int pointed_size; + + char *pointed_tag_name; + +}; + +static struct pending_param pending_params[MAX_PENDING_PARAMS]; +static int pending_param_count = 0; +static int declarator_depth = 0; + +static int local_symbol_exists_in_current_scope (const char *name, int start_count); + +static void clear_pending_params (void) { + + int i; + + for (i = 0; i < pending_param_count; i++) { + + if (pending_params[i].name) { + + free (pending_params[i].name); + pending_params[i].name = 0; + + } + + pending_params[i].size = 0; + pending_params[i].align = 0; + pending_params[i].is_unsigned = 0; + pending_params[i].is_floating = 0; + pending_params[i].pointer_depth = 0; + pending_params[i].pointed_size = 0; + + if (pending_params[i].pointed_tag_name) { + + free (pending_params[i].pointed_tag_name); + pending_params[i].pointed_tag_name = 0; + + } + + } + + pending_param_count = 0; + +} + +static void add_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size) { + + if (!name || !*name) { + return; + } + + if (pending_param_count >= MAX_PENDING_PARAMS) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many function parameters"); + return; + + } + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + if (align < 1) { + align = type_alignment (size); + } + + pending_params[pending_param_count].name = xstrdup (name); + pending_params[pending_param_count].size = size; + pending_params[pending_param_count].align = align; + pending_params[pending_param_count].is_unsigned = is_unsigned ? 1 : 0; + pending_params[pending_param_count].is_floating = is_floating ? 1 : 0; + pending_params[pending_param_count].pointer_depth = pointer_depth; + pending_params[pending_param_count].pointed_size = pointed_size > 0 ? pointed_size : 0; + pending_params[pending_param_count].pointed_tag_name = (pointer_depth > 0 && parsed_type_tag_name[0]) ? xstrdup (parsed_type_tag_name) : 0; + + pending_param_count++; + +} + +static struct local_symbol *find_local_symbol (const char *name) { + + int i; + + if (!name) { + return 0; + } + + for (i = local_symbol_count - 1; i >= 0; i--) { + + if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) { + return &local_symbols[i]; + } + + } + + return 0; + +} + +static void set_local_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym) { + + sym->pointer_depth = pointer_depth; + sym->pointed_size = pointed_size; + + if (sym->pointed_tag_name) { + + free (sym->pointed_tag_name); + sym->pointed_tag_name = 0; + + } + + if (pointer_depth > 0 && parsed_type_tag_name[0]) { + sym->pointed_tag_name = xstrdup (parsed_type_tag_name); + } + + } + +} + +static void install_pending_params_as_locals (void) { + + long offset = current_function_returns_aggregate ? 12 : 8; + int i; + + for (i = 0; i < pending_param_count; i++) { + + int slot_size; + + if (!pending_params[i].name) { + continue; + } + + if (local_symbol_count >= MAX_LOCAL_SYMBOLS) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many local symbols"); + break; + + } + + if (local_symbol_exists_in_current_scope (pending_params[i].name, 0)) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", pending_params[i].name); + } + + local_symbols[local_symbol_count].name = xstrdup (pending_params[i].name); + local_symbols[local_symbol_count].size = pending_params[i].size; + local_symbols[local_symbol_count].align = pending_params[i].align; + local_symbols[local_symbol_count].offset = (int) offset; + local_symbols[local_symbol_count].is_static = 0; + local_symbols[local_symbol_count].static_label = 0; + local_symbols[local_symbol_count].is_unsigned = pending_params[i].is_unsigned; + local_symbols[local_symbol_count].is_floating = pending_params[i].is_floating; + local_symbols[local_symbol_count].pointer_depth = pending_params[i].pointer_depth; + local_symbols[local_symbol_count].pointed_size = pending_params[i].pointed_size; + local_symbols[local_symbol_count].pointed_tag_name = pending_params[i].pointed_tag_name ? xstrdup (pending_params[i].pointed_tag_name) : 0; + local_symbols[local_symbol_count].tag_name = 0; + + local_symbol_count++; + slot_size = pending_params[i].size; + + if (slot_size < DATA_PTR) { + slot_size = DATA_PTR; + } + + slot_size = (int) align_up_long (slot_size, DATA_PTR); + offset += slot_size; + + } + +} + +#define MAX_LOCAL_INITS 1024 + +#define LOCAL_INIT_CONST 0 +#define LOCAL_INIT_ADDRESS 1 +#define LOCAL_INIT_STACK 2 +#define LOCAL_INIT_GLOBAL 3 + +struct local_init { + + long offset; + + int size; + int kind; + + char *symbol; + int64_s value; + + long source_offset; + int source_size; + +}; + +static void switch_section (int sec) { + + if (!state->ofp || current_section == sec) { + return; + } + + if (sec == SECTION_TEXT) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "section .text\n"); + } else { + fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".code" : ".text")); + } + + } else if (sec == SECTION_DATA) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "section .data\n"); + } else { + fprintf (state->ofp, ".data\n"); + } + + } else if (sec == SECTION_BSS) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "section .bss\n"); + } else { + fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".data?" : ".bss")); + } + + } + + current_section = sec; + +} + +static int token_is_ms_int_type_name (void) { + + if (tok.kind != TOK_IDENT || !tok.ident) { + return 0; + } + + return strcmp (tok.ident, "__int8") == 0 || strcmp (tok.ident, "__int16") == 0 || + strcmp (tok.ident, "__int32") == 0 || strcmp (tok.ident, "__int64") == 0; + +} + +static int is_type_start (enum token_kind k) { + + if (k == TOK_IDENT && token_is_ms_int_type_name ()) { + return 1; + } + + switch (k) { + + case TOK_AUTO: case TOK_REGISTER: case TOK_STATIC: + case TOK_EXTERN: case TOK_TYPEDEF: case TOK_INLINE: + case TOK_CONST: case TOK_VOLATILE: case TOK_RESTRICT: + case TOK_SIGNED: case TOK_UNSIGNED: + case TOK_SHORT: case TOK_LONG: + case TOK_CHAR: case TOK_INT: case TOK_VOID: + case TOK_FLOAT: case TOK_DOUBLE: + case TOK_STRUCT: case TOK_UNION: case TOK_ENUM: + + return 1; + + default: + + break; + + } + + if (k == TOK_IDENT && is_current_typedef_name ()) { + return 1; + } + + return 0; + +} + +/*static void next (void) { + get_token (); +}*/ + +static int expect (enum token_kind k, const char *what); + +static void parse_declarator (char **out_name); +static void parse_type_spec (void); + +int token_starts_type_name (void) { + return is_type_start (tok.kind); +} + +int parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer) { + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_inline = parsed_type_is_inline; + int saved_field_count = parsed_field_count; + int saved_fields[MAX_AGG_FIELDS]; + + int saved_declarator_is_pointer = declarator_is_pointer; + int saved_declarator_has_array = declarator_has_array; + int saved_declarator_has_function = declarator_has_function; + int saved_declarator_array_unsized = declarator_array_unsized; + long saved_declarator_array_count = declarator_array_count; + + char *name = 0; + int size = DATA_INT & 0x1f; + int is_unsigned = 0; + int is_pointer = 0; + int ok = 0; + int i; + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + if (is_type_start (tok.kind)) { + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; + + parse_type_spec (); + + size = parsed_type_size & 0x1f; + is_unsigned = parsed_type_is_unsigned; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&name); + } + + if (declarator_is_pointer) { + + size = DATA_PTR; + + is_unsigned = 1; + is_pointer = 1; + + } + + if (tok.kind == TOK_RPAREN) { + + get_token (); + ok = 1; + + } else { + expect (TOK_RPAREN, ")"); + } + + if (name) { + free (name); + } + + } + + last_cast_type_tag_name[0] = '\0'; + last_cast_type_object_size = parsed_type_size & 0x1f; + + if (parsed_type_tag_name[0]) { + + strncpy (last_cast_type_tag_name, parsed_type_tag_name, sizeof (last_cast_type_tag_name) - 1); + last_cast_type_tag_name[sizeof (last_cast_type_tag_name) - 1] = '\0'; + + } + + if (parsed_type_is_aggregate && parsed_type_size > 0) { + last_cast_type_object_size = parsed_type_size; + } + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + clear_parsed_fields (); + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + parsed_field_sizes[i] = saved_fields[i]; + } + + parsed_field_count = saved_field_count; + + declarator_is_pointer = saved_declarator_is_pointer; + declarator_has_array = saved_declarator_has_array; + declarator_has_function = saved_declarator_has_function; + declarator_array_unsized = saved_declarator_array_unsized; + declarator_array_count = saved_declarator_array_count; + + if (out_size) { + *out_size = size; + } + + if (out_is_unsigned) { + *out_is_unsigned = is_unsigned; + } + + if (out_is_pointer) { + *out_is_pointer = is_pointer; + } + + return ok; + +} + +static int parse_deref_cast_type_name (int *out_size) { + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_inline = parsed_type_is_inline; + int saved_field_count = parsed_field_count; + int saved_fields[MAX_AGG_FIELDS]; + int saved_declarator_is_pointer = declarator_is_pointer; + int saved_declarator_has_array = declarator_has_array; + int saved_declarator_has_function = declarator_has_function; + int saved_declarator_array_unsized = declarator_array_unsized; + + long saved_declarator_array_count = declarator_array_count; + char *name = 0; + + int size = DATA_INT & 0x1f; + int ok = 0; + int i; + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + if (is_type_start (tok.kind)) { + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; + + parse_type_spec (); + size = parsed_type_size & 0x1f; + + if (tok.kind != TOK_RPAREN) { + + parse_declarator (&name); + + if (declarator_is_pointer) { + size = DATA_PTR; + } + + } + + if (tok.kind == TOK_RPAREN) { + + get_token (); + ok = 1; + + } else { + expect (TOK_RPAREN, ")"); + } + + if (name) { + free (name); + } + + } + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + clear_parsed_fields (); + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + parsed_field_sizes[i] = saved_fields[i]; + } + + parsed_field_count = saved_field_count; + + declarator_is_pointer = saved_declarator_is_pointer; + declarator_has_array = saved_declarator_has_array; + declarator_has_function = saved_declarator_has_function; + declarator_array_unsized = saved_declarator_array_unsized; + declarator_array_count = saved_declarator_array_count; + + if (out_size) { + *out_size = size; + } + + return ok; + +} + +static int _accept (enum token_kind k) { + + if (tok.kind == k) { + + get_token (); + return 1; + + } + + return 0; + +} + +static int expect (enum token_kind k, const char *what) { + + if (tok.kind == k) { + + get_token (); + return 1; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected %s", what); + return 0; + +} + +static int token_is_ident (void) { + return tok.kind == TOK_IDENT; +} + +static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3); +static void emit_load_assignment_rhs_expression_to_reg (const char *reg); + +static void consume_comma_expression_tail_before_semi (void) { + + while (tok.kind == TOK_COMMA) { + + get_token (); + emit_load_assignment_rhs_expression_to_reg ("eax"); + + } + +} + +static void expect_semi_or_recover (void) { + + consume_comma_expression_tail_before_semi (); + + if (expect (TOK_SEMI, ";")) { + return; + } + + skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF); + + if (tok.kind == TOK_SEMI) { + get_token (); + } + +} + +static int current_ident_is_known_value (void) { + + int64_s ignored; + + if (tok.kind != TOK_IDENT) { + return 0; + } + + if (find_local_symbol (tok.ident)) { + return 1; + } + + if (find_global_symbol (tok.ident) >= 0) { + return 1; + } + + if (resolve_enum_constant (tok.ident, &ignored)) { + return 1; + } + + return 0; + +} + +static int recover_unknown_rhs_identifier (void) { + + const char *name; + const char *start; + const char *caret; + + unsigned long line; + + if (tok.kind != TOK_IDENT || current_ident_is_known_value ()) { + return 0; + } + + name = tok.ident ? tok.ident : ""; + start = tok.start; + caret = tok.caret; + line = get_line_number (); + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name); + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF); + + if (tok.kind == TOK_RPAREN) { + get_token (); + } + + } + + return 1; + +} + +static char *take_ident (void) { + + char *name; + + if (!token_is_ident ()) { + return 0; + } + + name = xstrdup (tok.ident); + + last_declarator_name_line = get_line_number (); + last_declarator_name_start = tok.start; + last_declarator_name_caret = tok.caret; + + if (capture_declarator_name_location && !captured_declarator_name_location) { + + captured_declarator_name_location = 1; + captured_declarator_name_line = last_declarator_name_line; + captured_declarator_name_start = last_declarator_name_start; + captured_declarator_name_caret = last_declarator_name_caret; + + } + + get_token (); + return name; + +} + +static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3) { + + int paren = 0, brace = 0, brack = 0; + + while (tok.kind != TOK_EOF) { + + if (paren == 0 && brace == 0 && brack == 0) { + + if (tok.kind == stop1 || tok.kind == stop2 || tok.kind == stop3) { + return; + } + + } + + if (tok.kind == TOK_LPAREN) { + paren++; + } else if (tok.kind == TOK_RPAREN) { + + if (paren > 0) { + paren--; + } else { + + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } + + } + + } else if (tok.kind == TOK_LBRACE) { + brace++; + } else if (tok.kind == TOK_RBRACE) { + + if (brace > 0) { + brace--; + } else { + + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } + + } + + } else if (tok.kind == TOK_LBRACK) { + brack++; + } else if (tok.kind == TOK_RBRACK) { + + if (brack > 0) { + brack--; + } else { + + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } + + } + + } + + get_token (); + + } + +} + +/*static void consume_redundant_rparens_before_statement (void) { + + while (tok.kind == TOK_RPAREN) { + get_token (); + } + +}*/ + +static void skip_initializer (void) { + + if (tok.kind == TOK_LBRACE) { + skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF); + } else { + skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF); + } + +} + +static void parse_enum_body (void) { + + int64_s value; + zext64 (&value, 0); + + expect (TOK_LBRACE, "{"); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + if (!token_is_ident ()) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected enum constant"); + + get_token (); + continue; + + } else { + + char *name; + + const char *name_start; + const char *name_caret; + + name = xstrdup (tok.ident); + + name_start = tok.start; + name_caret = tok.caret; + + get_token (); + + if (_accept (TOK_ASSIGN)) { + + enum token_kind kill[4]; + + kill[0] = TOK_COMMA; + kill[1] = TOK_RBRACE; + kill[2] = TOK_EOF; + kill[3] = 0; + + value = expr_const64 (kill); + + } + + save_enum_constant (name, value, name_start, name_caret); + free (name); + + inc64 (&value); + + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_RBRACE, "}"); + +} + +static void parse_declarator (char **out_name); +static void parse_declarator_inner (char **out_name); + +static void parse_type_spec (void) { + + int saw = 0; + + int saw_float = 0; + int saw_double = 0; + int saw_void = 0; + + int saw_signed = 0; + int saw_unsigned = 0; + int saw_real_type = 0; + + parsed_storage_class = STORAGE_NONE; + parsed_type_is_aggregate = 0; + parsed_type_is_unsigned = 0; + parsed_type_has_tag = 0; + parsed_type_tag_name[0] = '\0'; + parsed_type_is_array_typedef = 0; + parsed_type_array_count = 1; + parsed_type_array_element_size = DATA_NONE; + parsed_type_is_inline = 0; + parsed_type_is_void = 0; + parsed_type_only_qualifiers = 0; + parsed_type_size = DATA_NONE; + + clear_parsed_fields (); + + while (tok.kind == TOK_AUTO || tok.kind == TOK_REGISTER || tok.kind == TOK_STATIC || + tok.kind == TOK_EXTERN || tok.kind == TOK_TYPEDEF || tok.kind == TOK_CONST || + tok.kind == TOK_VOLATILE || tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED || + tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR || + tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT || + tok.kind == TOK_DOUBLE || tok.kind == TOK_INLINE || tok.kind == TOK_RESTRICT || + token_is_ms_int_type_name ()) { + + saw = 1; + + if (tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED || + tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR || + tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT || + tok.kind == TOK_DOUBLE || token_is_ms_int_type_name ()) { + saw_real_type = 1; + } + + if (tok.kind == TOK_EXTERN) { + + if (parsed_storage_class == STORAGE_EXTERN) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'extern'"); + } else if (parsed_storage_class != STORAGE_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers"); + } else { + parsed_storage_class = STORAGE_EXTERN; + } + + } else if (tok.kind == TOK_STATIC) { + + if (parsed_storage_class == STORAGE_STATIC) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'static'"); + } else if (parsed_storage_class != STORAGE_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers"); + } else { + parsed_storage_class = STORAGE_STATIC; + } + + } else if (tok.kind == TOK_TYPEDEF) { + + if (parsed_storage_class == STORAGE_TYPEDEF) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'typedef'"); + } else if (parsed_storage_class != STORAGE_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers"); + } else { + parsed_storage_class = STORAGE_TYPEDEF; + } + + } else if (tok.kind == TOK_INLINE) { + parsed_type_is_inline = 1; + } else if (tok.kind == TOK_CHAR) { + + if (parsed_type_size == DATA_CHAR) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'char'"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_CHAR; + } + + } else if (tok.kind == TOK_SHORT) { + + if (parsed_type_size == DATA_SHORT) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'short'"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_SHORT; + } + + } else if (tok.kind == TOK_INT) { + + if (parsed_type_size == DATA_INT) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'int'"); + } else if (parsed_type_size != DATA_NONE) { + /*report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");*/ + } else { + parsed_type_size = DATA_INT; + } + + } else if (tok.kind == TOK_LONG) { + + if (parsed_type_size == DATA_LONG) { + parsed_type_size = DATA_LLONG; + } else if (parsed_type_size == DATA_LLONG) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many 'long' keywords"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_LONG; + } + + } else if (tok.kind == TOK_SIGNED) { + saw_signed = 1; + } else if (tok.kind == TOK_UNSIGNED) { + saw_unsigned = 1; + } else if (tok.kind == TOK_FLOAT) { + + if (parsed_type_size == DATA_FLOAT) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'float'"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + + parsed_type_size = DATA_FLOAT; + saw_float = 1; + + } + + } else if (tok.kind == TOK_DOUBLE) { + + if (parsed_type_size == DATA_DOUBLE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'double'"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + + parsed_type_size = DATA_DOUBLE; + saw_double = 1; + + } + + } else if (tok.kind == TOK_VOID) { + + if (parsed_type_size == DATA_VOID) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'void'"); + } else if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + + parsed_type_size = DATA_VOID; + parsed_type_is_void = 1; + + saw_void = 1; + + } + + } else if (token_is_ms_int_type_name ()) { + + if (strcmp (tok.ident, "__int8") == 0) { + + if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_CHAR & 0x1f; + } + + } else if (strcmp (tok.ident, "__int16") == 0) { + + if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_SHORT & 0x1f; + } + + } else if (strcmp (tok.ident, "__int32") == 0) { + + if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_INT & 0x1f; + } + + } else { + + if (parsed_type_size != DATA_NONE) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers"); + } else { + parsed_type_size = DATA_LLONG & 0x1f; + } + + } + + } + + get_token (); + + } + + /* + * Be tolerant of project typedef chains where the base typedef name is + * not already known to this parser. In + * + * typedef some_base new_name; + * typedef some_base *new_ptr; + * + * the declarator name is new_name/new_ptr, not some_base. Without + * consuming the unknown base identifier here, parse_declarator() records + * some_base as the typedef name and the real declarator is then parsed as + * junk. + */ + if (parsed_storage_class == STORAGE_TYPEDEF && !saw_real_type && + tok.kind == TOK_IDENT && !is_current_typedef_name ()) { + + get_token (); + + saw = 1; + saw_real_type = 1; + + parsed_type_size = DATA_INT; + + } + + if (parsed_type_size == DATA_NONE) { + + parsed_type_only_qualifiers = (saw && !saw_real_type) ? 1 : 0; + parsed_type_size = DATA_INT; + + } else if (parsed_type_size == DATA_LONG && state->long64) { + parsed_type_size = DATA_LLONG; + } + + parsed_type_size &= 0x1f; + + if (is_current_typedef_name ()) { + + struct typedef_entry *entry; + + entry = find_typedef_name (tok.ident); + get_token (); + + load_typedef_name (entry); + + if (entry && entry->is_aggregate && entry->tag_name && entry->tag_name[0]) { + + strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1); + parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0'; + + parsed_type_has_tag = 1; + + } + + parsed_type_only_qualifiers = 0; + return; + + } + + if (tok.kind == TOK_STRUCT || tok.kind == TOK_UNION) { + + int is_union = (tok.kind == TOK_UNION); + int has_tag = 0; + + int aggregate_size = 0; + int aggregate_align = 1; + + int aggregate_fields[MAX_AGG_FIELDS]; + int aggregate_field_count = 0; + + int union_init_fields[MAX_AGG_FIELDS]; + int union_init_field_count = 0; + int union_init_size = 0; + + struct aggregate_tag_entry *tag_entry = 0; + + int aggregate_storage_class = parsed_storage_class; + int aggregate_is_inline = parsed_type_is_inline; + + char *tag_name = 0; + int member_info_start = member_info_count; + + parsed_type_size = DATA_PTR; + parsed_type_is_void = 0; + + clear_parsed_fields (); + get_token (); + + if (token_is_ident ()) { + + tag_name = xstrdup (tok.ident); + strncpy (parsed_type_tag_name, tok.ident, sizeof (parsed_type_tag_name) - 1); + + parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0'; + has_tag = 1; + + get_token (); + + } + + if (tok.kind == TOK_LBRACE) { + + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + int member_type_size; + int member_type_is_floating; + int member_is_aggregate; + int member_has_tag; + + int member_fields[MAX_AGG_FIELDS]; + int member_field_count; + + int i; + + if (!is_type_start (tok.kind)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member declaration"); + + skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF); + _accept (TOK_SEMI); + + continue; + + } + + parse_type_spec (); + + member_type_size = parsed_type_size; + member_type_is_floating = parsed_type_is_floating; + member_is_aggregate = parsed_type_is_aggregate; + member_has_tag = parsed_type_has_tag; + member_field_count = parsed_field_count; + + for (i = 0; i < member_field_count; i++) { + member_fields[i] = parsed_field_sizes[i]; + } + + if (tok.kind == TOK_SEMI) { + + if (member_is_aggregate && !member_has_tag) { + + int member_init_size = fields_storage_size (member_fields, member_field_count); + + if (is_union) { + + if (member_type_size > aggregate_size) { + aggregate_size = member_type_size; + } + + if (union_init_field_count == 0) { + + union_init_field_count = member_field_count; + + for (i = 0; i < member_field_count; i++) { + union_init_fields[i] = member_fields[i]; + } + + union_init_size = member_init_size; + + } + + } else { + + aggregate_size += member_type_size; + + for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) { + aggregate_fields[aggregate_field_count++] = member_fields[i]; + } + + if (member_type_size > member_init_size && aggregate_field_count < MAX_AGG_FIELDS) { + aggregate_fields[aggregate_field_count++] = -(member_type_size - member_init_size); + } + + } + + } + + } else { + + for (;;) { + + char *member = 0; + + int member_size, repeat; + int init_size; + + int member_info_elem_size; + int member_align; + + parse_declarator (&member); + + if (declarator_is_pointer) { + + member_size = DATA_PTR; + member_field_count = 1; + member_fields[0] = DATA_PTR; + + } else { + + member_size = member_type_size; + + if (member_field_count <= 0) { + + member_field_count = 1; + member_fields[0] = member_type_size; + + } + + } + + member_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (member_type_size); + + if (member_align > aggregate_align) { + aggregate_align = member_align; + } + + repeat = 1; + + if (declarator_has_array) { + + repeat = (int) declarator_array_count; + member_size *= declarator_array_count; + + } + + if (_accept (TOK_COLON)) { + skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_RBRACE); + } + + if (declarator_has_array && declarator_is_pointer) { + member_info_elem_size = DATA_PTR; + } else { + + member_info_elem_size = declarator_is_pointer ? + (declarator_pointer_depth > 1 ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))) : + (declarator_has_array ? member_type_size : member_size); + + } + + init_size = fields_storage_size (member_fields, member_field_count); + + if (is_union) { + + remember_member_info_ex (member, 0, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating); + + if (member_size > aggregate_size) { + aggregate_size = member_size; + } + + if (union_init_field_count == 0) { + + union_init_field_count = member_field_count; + + for (i = 0; i < member_field_count; i++) { + union_init_fields[i] = member_fields[i]; + } + + union_init_size = init_size; + + } + + } else { + + int member_offset = (int) align_up_long (aggregate_size, member_align); + int padding = member_offset - aggregate_size; + + int r; + + if (padding > 0 && aggregate_field_count < MAX_AGG_FIELDS) { + aggregate_fields[aggregate_field_count++] = -padding; + } + + aggregate_size = member_offset; + + remember_member_info_ex (member, member_offset, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating); + aggregate_size += member_size; + + for (r = 0; r < repeat; r++) { + + for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) { + aggregate_fields[aggregate_field_count++] = member_fields[i]; + } + + } + + if (member_size > init_size * repeat && aggregate_field_count < MAX_AGG_FIELDS) { + aggregate_fields[aggregate_field_count++] = -(member_size - init_size * repeat); + } + + } + + if (member) { + free (member); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_SEMI, ";"); + + } + + expect (TOK_RBRACE, "}"); + + if (aggregate_size <= 0) { + aggregate_size = DATA_CHAR & 0x1f; + } + + aggregate_size = (int) align_up_long (aggregate_size, aggregate_align); + + { + + const char *owner_name = 0; + char anon_owner_name[64]; + + int mi; + + if (has_tag) { + owner_name = tag_name; + } else { + + sprintf (anon_owner_name, "", anonymous_aggregate_owner_id++); + owner_name = anon_owner_name; + + } + + for (mi = member_info_start; mi < member_info_count; mi++) { + + if (member_infos[mi].owner_size == 0) { + member_infos[mi].owner_size = aggregate_size; + } + + if (owner_name && member_infos[mi].owner_tag_name == 0) { + member_infos[mi].owner_tag_name = xstrdup (owner_name); + } + + } + + if (!has_tag && owner_name) { + + strncpy (parsed_type_tag_name, owner_name, sizeof (parsed_type_tag_name) - 1); + parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0'; + + } + + } + + parsed_type_size = aggregate_size; + parsed_type_is_aggregate = 1; + parsed_type_is_void = 0; + parsed_type_has_tag = has_tag; + + clear_parsed_fields (); + + if (is_union) { + + int i; + + for (i = 0; i < union_init_field_count; i++) { + append_parsed_field (union_init_fields[i]); + } + + if (aggregate_size > union_init_size) { + append_parsed_field (-(aggregate_size - union_init_size)); + } + + } else { + + int i; + + for (i = 0; i < aggregate_field_count; i++) { + append_parsed_field (aggregate_fields[i]); + } + + } + + if (tag_name) { + + save_aggregate_tag (tag_name, is_union, parsed_type_size, parsed_field_sizes, parsed_field_count); + update_typedef_name_from_aggregate_tag (tag_name, parsed_type_size, parsed_field_sizes, parsed_field_count); + + } + + } else { + + tag_entry = find_aggregate_tag (tag_name, is_union); + + if (tag_entry) { + load_aggregate_tag_fields (tag_entry); + } else { + + parsed_type_size = DATA_PTR; + append_parsed_field (DATA_PTR); + + } + + } + + if (tag_name) { + free (tag_name); + } + + parsed_storage_class = aggregate_storage_class; + parsed_type_is_inline = aggregate_is_inline; + + return; + + } + + if (tok.kind == TOK_ENUM) { + + parsed_type_size = DATA_INT & 0x1f; + parsed_type_is_unsigned = 0; + + get_token (); + + if (token_is_ident ()) { + get_token (); + } + + if (tok.kind == TOK_LBRACE) { + parse_enum_body (); + } + + append_parsed_field (DATA_INT & 0x1f); + return; + + } + + /*if (saw_void) { + parsed_type_size = DATA_INT; + }*/ + + parsed_type_is_floating = (saw_float || saw_double) ? 1 : 0; + + if (saw_signed && saw_unsigned) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "both signed and unsigned specified"); + } + + if ((saw_signed || saw_unsigned) && (saw_float || saw_double || saw_void)) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "signed/unsigned invalid for this type"); + } + + parsed_type_is_unsigned = saw_unsigned ? 1 : 0; + append_parsed_field (parsed_type_size); + + if (!saw) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type"); + get_token (); + + } + +} + +static int declarator_object_size (int base_size); + +static void parse_direct_declarator (char **out_name) { + + if (_accept (TOK_LPAREN)) { + + parse_declarator_inner (out_name); + expect (TOK_RPAREN, ")"); + + if (declarator_is_pointer && !declarator_has_function) { + declarator_function_is_pointer = 1; + } + + } else if (token_is_ident ()) { + + if (out_name && !*out_name) { + *out_name = take_ident (); + } else { + get_token (); + } + + } + + for (;;) { + + if (_accept (TOK_LBRACK)) { + + long count = 1; + declarator_has_array = 1; + + if (tok.kind != TOK_RBRACK) { + + enum token_kind kill[3]; + int v; + + kill[0] = TOK_RBRACK; + kill[1] = TOK_EOF; + kill[2] = 0; + + v = expr_const (kill); + + if (v > 0) { + count = v; + } + + } else { + declarator_array_unsized = 1; + } + + if (declarator_array_count <= 0) { + declarator_array_count = 1; + } + + declarator_array_count *= count; + declarator_last_array_count = count; + + expect (TOK_RBRACK, "]"); + continue; + + } + + if (_accept (TOK_LPAREN)) { + + declarator_has_function = 1; + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + if (tok.kind == TOK_ELLIPSIS) { + + declarator_function_is_variadic = 1; + get_token (); + + } else if (is_type_start (tok.kind)) { + + long saved_array_count = declarator_array_count; + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_pointer = declarator_is_pointer; + int saved_pointer_depth = declarator_pointer_depth; + int saved_has_array = declarator_has_array; + int saved_has_function = declarator_has_function; + int saved_function_is_pointer = declarator_function_is_pointer; + int saved_function_param_count = declarator_function_param_count; + int saved_function_has_prototype = declarator_function_has_prototype; + int saved_array_unsized = declarator_array_unsized; + int saved_is_inline = parsed_type_is_inline; + + char *param_name = 0; + int param_base_size, param_size; + + int count_this_param = 0; + int saw_void_param_list = 0; + + parse_type_spec (); + param_base_size = parsed_type_size; + + /* + * Parameter declarators must not inherit the outer + * declarator state. In particular, in declarations + * such as: + * + * FILE **__gtin(void); + * + * the function return type leaves declarator_is_pointer + * set before the parameter list is parsed. If that flag + * is still set while looking at the abstract void + * parameter list, void is mistaken for a real pointer + * parameter and the prototype is recorded as taking one + * argument. + */ + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_function_is_pointer = 0; + declarator_array_unsized = 0; + declarator_array_count = 1; + declarator_last_array_count = 1; + + if (parsed_type_only_qualifiers && token_is_ident ()) { + get_token (); + } + + if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) { + parse_declarator (¶m_name); + } + + if (!parsed_type_is_void || declarator_is_pointer || declarator_has_array || declarator_has_function || param_name) { + + count_this_param = 1; + param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size); + + if (declarator_depth == 1) { + + add_pending_param (param_name, param_size, type_alignment (param_size), + (declarator_is_pointer || parsed_type_is_array_typedef ? 0 : parsed_type_is_unsigned), + (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? 0 : parsed_type_is_floating, + declarator_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f)); + + } + + } else { + saw_void_param_list = 1; + } + + if (param_name) { + free (param_name); + } + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + declarator_is_pointer = saved_is_pointer; + declarator_pointer_depth = saved_pointer_depth; + declarator_has_array = saved_has_array; + declarator_has_function = saved_has_function; + declarator_function_is_pointer = saved_function_is_pointer; + declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0); + declarator_function_has_prototype = saved_function_has_prototype || count_this_param || saw_void_param_list; + declarator_array_count = saved_array_count; + + declarator_array_unsized = saved_array_unsized; + + } else if (token_is_ident ()) { + + char *maybe_type_name = xstrdup (tok.ident); + int unknown_typedef_pointer = 0; + int consumed_as_prototype_param = 0; + + get_token (); + + while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) { + get_token (); + } + + while (tok.kind == TOK_STAR) { + + unknown_typedef_pointer = 1; + get_token (); + + while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) { + get_token (); + } + + } + + if (token_is_ident ()) { + + char *param_name = xstrdup (tok.ident); + int param_size = unknown_typedef_pointer ? DATA_PTR : DATA_PTR; + + get_token (); + + if (declarator_depth == 1) { + add_pending_param (param_name, param_size, type_alignment (param_size), 0, 0, unknown_typedef_pointer ? 1 : 0, DATA_INT & 0x1f); + } + + free (param_name); + + declarator_function_param_count++; + declarator_function_has_prototype = 1; + consumed_as_prototype_param = 1; + + } + + if (!consumed_as_prototype_param) { + declarator_function_param_count++; + } + + if (maybe_type_name) { + free (maybe_type_name); + } + + } else { + skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + continue; + } + + break; + + } + +} + +static void parse_declarator_inner (char **out_name) { + + while (tok.kind == TOK_STAR) { + + declarator_is_pointer = 1; + declarator_pointer_depth++; + + get_token (); + + while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) { + get_token (); + } + + } + + parse_direct_declarator (out_name); + +} + +static void parse_declarator (char **out_name) { + + int top_level = (declarator_depth == 0); + + int saved_capture_declarator_name_location = capture_declarator_name_location; + int saved_captured_declarator_name_location = captured_declarator_name_location; + + const char *saved_captured_declarator_name_start = captured_declarator_name_start; + const char *saved_captured_declarator_name_caret = captured_declarator_name_caret; + + unsigned long saved_captured_declarator_name_line = captured_declarator_name_line; + + if (top_level) { + + clear_pending_params (); + + capture_declarator_name_location = 1; + captured_declarator_name_location = 0; + captured_declarator_name_start = 0; + captured_declarator_name_caret = 0; + captured_declarator_name_line = 0; + + } + + declarator_depth++; + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_function_is_pointer = 0; + declarator_function_param_count = 0; + declarator_function_has_prototype = 0; + declarator_function_is_variadic = 0; + declarator_array_unsized = 0; + declarator_array_count = 1; + declarator_last_array_count = 1; + + parse_declarator_inner (out_name); + + if (top_level && parsed_type_is_array_typedef && !declarator_is_pointer && !declarator_has_array && !declarator_has_function) { + + declarator_has_array = 1; + + declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1; + declarator_last_array_count = declarator_array_count; + + if (parsed_type_array_element_size > 0) { + parsed_type_size = parsed_type_array_element_size; + } + + } + + declarator_depth--; + + if (top_level) { + + if (captured_declarator_name_location) { + + last_declarator_name_line = captured_declarator_name_line; + last_declarator_name_start = captured_declarator_name_start; + last_declarator_name_caret = captured_declarator_name_caret; + + } + + capture_declarator_name_location = saved_capture_declarator_name_location; + captured_declarator_name_location = saved_captured_declarator_name_location; + captured_declarator_name_start = saved_captured_declarator_name_start; + captured_declarator_name_caret = saved_captured_declarator_name_caret; + captured_declarator_name_line = saved_captured_declarator_name_line; + + } + +} + +static void apply_typedef_array_to_declarator (void) { + + if (parsed_type_is_array_typedef && !declarator_is_pointer && + !declarator_has_array && !declarator_has_function) { + + declarator_has_array = 1; + declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1; + declarator_last_array_count = declarator_array_count; + + if (parsed_type_array_element_size > 0) { + parsed_type_size = parsed_type_array_element_size; + } + + } + +} + +static void parse_old_style_param_decls (void) { + + while (is_type_start (tok.kind)) { + + parse_type_spec (); + + for (;;) { + + char *name = 0; + parse_declarator (&name); + + if (name) { + free (name); + } + + if (_accept (TOK_ASSIGN)) { + skip_initializer (); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_SEMI, ";"); + + } + +} + +static void emit_extern_symbol (const char *name, int size, int is_function); +static void emit_extern_reference_symbol (const char *name, int size); + +static void emit_function_start (const char *name, int is_static) { + + const char *asm_name; + + if (!state->ofp) { + return; + } + + asm_name = asm_global_symbol_name (name); + switch_section (SECTION_TEXT); + + if (state->syntax & ASM_SYNTAX_MASM) { + + if (!is_static) { + fprintf (state->ofp, "public %s\n", asm_name); + } + + } else if (state->syntax & ASM_SYNTAX_NASM) { + + if (!is_static) { + fprintf (state->ofp, "global %s\n", asm_name); + } + + } else { + + if (!is_static) { + fprintf (state->ofp, ".globl %s\n", asm_name); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, "%s:\n", asm_name); + fprintf (state->ofp, " push ebp\n"); + fprintf (state->ofp, " mov ebp, esp\n"); + + } else { + + fprintf (state->ofp, "%s:\n", asm_name); + fprintf (state->ofp, " pushl %%ebp\n"); + fprintf (state->ofp, " movl %%esp, %%ebp\n"); + + } + +} + +static void emit_pending_return_jump (void) { + + if (!pending_return_jump) { + return; + } + + if (!state->ofp) { + + pending_return_jump = 0; + return; + + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, " jmp L%d\n", current_return_label); + } else { + fprintf (state->ofp, " jmp .L%d\n", current_return_label); + } + + pending_return_jump = 0; + +} + +static void emit_stack_adjust (long bytes, int allocate) { + + if (!state->ofp || bytes <= 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s esp, %ld\n", allocate ? "sub" : "add", bytes); + } else { + fprintf (state->ofp, " %s $%ld, %%esp\n", allocate ? "subl" : "addl", bytes); + } + +} + +static void format_intel_ebp_offset (char *buf, size_t bufsz, long offset) { + + if (!buf || bufsz == 0) { + return; + } + + if (offset < 0) { + sprintf (buf, "[ebp - %ld]", -offset); + } else if (offset > 0) { + sprintf (buf, "[ebp + %ld]", offset); + } else { + sprintf (buf, "[ebp]"); + } + +} + +static const char *format_nasm_memory_operand (char *buf, size_t bufsz, const char *operand) { + + if (!operand) { + return operand; + } + + if (!(state->syntax & ASM_SYNTAX_NASM)) { + return operand; + } + + if (operand[0] == '[') { + return operand; + } + + if (buf && bufsz > 0) { + + sprintf (buf, "[%s]", operand); + return buf; + + } + + return operand; + +} + +static void emit_local_store_const (long offset, int size, int64_s value) { + + char memref_hi[64], memref[64]; + unsigned long high, low; + + if (!state->ofp) { + return; + } + + low = value.low; + high = value.high; + + if (size > 8 && low == 0 && high == 0) { + + long pos; + + for (pos = 0; pos < size; pos += 4) { + emit_local_store_const (offset + pos, DATA_INT, value); + } + + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, %lu\n" : " mov byte ptr %s, %lu\n"), memref, low & ((1 << 8) - 1)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, %lu\n" : " mov word ptr %s, %lu\n"), memref, low & ((1 << 16) - 1)); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4); + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref, low & U32_MASK); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref_hi, high & U32_MASK); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref, low & U32_MASK); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb $%lu, %ld(%%ebp)\n", low & 0xffUL, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw $%lu, %ld(%%ebp)\n", low & 0xffffUL, offset); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset); + fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", high & U32_MASK, offset + 4); + + } else { + fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset); + } + + } + +} + +static void emit_local_store_address (long offset, const char *symbol) { + + const char *asm_symbol; + char memref[64]; + + if (!state->ofp || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_PTR); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, offset %s\n"), memref, asm_symbol); + + } else { + fprintf (state->ofp, " movl $%s, %ld(%%ebp)\n", asm_symbol, offset); + } + +} + +static void emit_local_store_stack (long dst_offset, int dst_size, long src_offset, int src_size) { + + char dst[64]; + char src[64]; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (dst, sizeof (dst), dst_offset); + format_intel_ebp_offset (src, sizeof (src), src_offset); + + if (dst_size == (DATA_CHAR & 0x1f)) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov al, byte %s\n" : " mov al, byte ptr %s\n"), src); + fprintf (state->ofp, " mov byte ptr %s, al\n", dst); + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + if (src_size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov ax, byte %s\n" : " movzx ax, byte ptr %s\n"), src); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov ax, word %s\n" : " mov ax, word ptr %s\n"), src); + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, ax\n" : " mov word ptr %s, ax\n"), dst); + + } else { + + if (src_size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte %s\n" : " movzx eax, byte ptr %s\n"), src); + } else if (src_size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word %s\n" : " movzx eax, word ptr %s\n"), src); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov eax, dword %s\n" : " mov eax, dword ptr %s\n"), src); + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, eax\n" : " mov dword ptr %s, eax\n"), dst); + + } + + } else { + + if (dst_size == (DATA_CHAR & 0x1f)) { + + fprintf (state->ofp, " movb %ld(%%ebp), %%al\n", src_offset); + fprintf (state->ofp, " movb %%al, %ld(%%ebp)\n", dst_offset); + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + fprintf (state->ofp, " movw %ld(%%ebp), %%ax\n", src_offset); + fprintf (state->ofp, " movw %%ax, %ld(%%ebp)\n", dst_offset); + + } else { + + fprintf (state->ofp, " movl %ld(%%ebp), %%eax\n", src_offset); + fprintf (state->ofp, " movl %%eax, %ld(%%ebp)\n", dst_offset); + + } + + } + +} + +static void emit_local_store_global (long dst_offset, int dst_size, const char *symbol) { + + const char *asm_symbol; + char dst[64]; + + if (!state->ofp || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, get_global_symbol_size (symbol)); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (dst, sizeof (dst), dst_offset); + + if (dst_size == (DATA_CHAR & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov al, byte [%s]\n", asm_symbol); + fprintf (state->ofp, " mov byte %s, al\n", dst); + + } else { + + fprintf (state->ofp, " mov al, byte ptr %s\n", asm_symbol); + fprintf (state->ofp, " mov byte ptr %s, al\n", dst); + + } + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov ax, word [%s]\n", asm_symbol); + fprintf (state->ofp, " mov word %s, ax\n", dst); + + } else { + + fprintf (state->ofp, " mov ax, word ptr %s\n", asm_symbol); + fprintf (state->ofp, " mov word ptr %s, ax\n", dst); + + } + + } else { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov eax, dword [%s]\n", asm_symbol); + fprintf (state->ofp, " mov dword %s, eax\n", dst); + + } else { + + fprintf (state->ofp, " mov eax, dword ptr %s\n", asm_symbol); + fprintf (state->ofp, " mov dword ptr %s, eax\n", dst); + + } + + } + + } else { + + if (dst_size == (DATA_CHAR & 0x1f)) { + + fprintf (state->ofp, " movb %s, %%al\n", asm_symbol); + fprintf (state->ofp, " movb %%al, %ld(%%ebp)\n", dst_offset); + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + fprintf (state->ofp, " movw %s, %%ax\n", asm_symbol); + fprintf (state->ofp, " movw %%ax, %ld(%%ebp)\n", dst_offset); + + } else { + + fprintf (state->ofp, " movl %s, %%eax\n", asm_symbol); + fprintf (state->ofp, " movl %%eax, %ld(%%ebp)\n", dst_offset); + + } + + } + +} + +static void emit_local_initializers (struct local_init *inits, int init_count) { + + int i; + + for (i = 0; i < init_count; i++) { + + if (inits[i].kind == LOCAL_INIT_ADDRESS) { + emit_local_store_address (inits[i].offset, inits[i].symbol); + } else if (inits[i].kind == LOCAL_INIT_STACK) { + emit_local_store_stack (inits[i].offset, inits[i].size, inits[i].source_offset, inits[i].source_size); + } else if (inits[i].kind == LOCAL_INIT_GLOBAL) { + emit_local_store_global (inits[i].offset, inits[i].size, inits[i].symbol); + } else { + emit_local_store_const (inits[i].offset, inits[i].size, inits[i].value); + } + + if (inits[i].symbol) { + + free (inits[i].symbol); + inits[i].symbol = 0; + + } + + } + +} + +static void emit_function_end (void) { + + if (!state->ofp) { + return; + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + + fprintf (state->ofp, "L%d:\n", current_return_label); + + if (current_function_returns_aggregate) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov eax, dword [ebp + 8]\n"); + } else { + fprintf (state->ofp, " mov eax, dword ptr [ebp + 8]\n"); + } + + } + + fprintf (state->ofp, " leave\n"); + fprintf (state->ofp, " ret\n"); + + } else { + + fprintf (state->ofp, ".L%d:\n", current_return_label); + + if (current_function_returns_aggregate) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, dword ptr [ebp + 8]\n"); + } else { + fprintf (state->ofp, " movl 8(%%ebp), %%eax\n"); + } + + } + + fprintf (state->ofp, " leave\n"); + fprintf (state->ofp, " ret\n"); + + } + +} + +static void emit_preserve_assignment64_regs (enum token_kind op) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " push ebx\n"); + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " push esi\n"); + fprintf (state->ofp, " push edi\n"); + + } + + } else { + + fprintf (state->ofp, " pushl %%ebx\n"); + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " pushl %%esi\n"); + fprintf (state->ofp, " pushl %%edi\n"); + + } + + } + +} + +static void emit_restore_assignment64_regs (enum token_kind op) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " pop edi\n"); + fprintf (state->ofp, " pop esi\n"); + + } + + fprintf (state->ofp, " pop ebx\n"); + + } else { + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " popl %%edi\n"); + fprintf (state->ofp, " popl %%esi\n"); + + } + + fprintf (state->ofp, " popl %%ebx\n"); + + } + +} + +static int is_string_token (void); +static void parse_string_initializer_values (int64_s *values, int max_values, int *count); + +static int parse_constexpr_null_member_address_after_lparen (int64_s *out) { + + char current_tag_name[128]; + + int current_object_size; + unsigned long total_offset = 0; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (!parse_cast_type_name (0, 0, 0)) { + return 0; + } + + current_tag_name[0] = '\0'; + + if (last_cast_type_tag_name[0]) { + + strncpy (current_tag_name, last_cast_type_tag_name, sizeof (current_tag_name) - 1); + current_tag_name[sizeof (current_tag_name) - 1] = '\0'; + + } + + current_object_size = last_cast_type_object_size; + + if (!((tok.kind == TOK_CINT || tok.kind == TOK_CUINT || tok.kind == TOK_CLONG || + tok.kind == TOK_CULONG || tok.kind == TOK_CLLONG || tok.kind == TOK_CULLONG) && + tok.val.i.low == 0 && tok.val.i.high == 0)) { + return 0; + } + + get_token (); + expect (TOK_RPAREN, ")"); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int size = DATA_INT & 0x1f; + int elem_size = DATA_INT & 0x1f; + int pointer_depth = 0; + int is_array = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, + "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, + current_tag_name[0] ? current_tag_name : 0, + &offset, &size, &elem_size, &pointer_depth, &is_array, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, + "unknown member '%s'", member); + + free (member); + return 1; + + } + + free (member); + total_offset += (unsigned long) offset; + + if (last_found_member_tag_name && last_found_member_tag_name[0]) { + + strncpy (current_tag_name, last_found_member_tag_name, sizeof (current_tag_name) - 1); + current_tag_name[sizeof (current_tag_name) - 1] = '\0'; + + } else { + current_tag_name[0] = '\0'; + } + + while (tok.kind == TOK_LBRACK) { + + enum token_kind kill[2]; + int64_s index; + + get_token (); + + kill[0] = TOK_RBRACK; + kill[1] = 0; + + index = expr_const64 (kill); + + expect (TOK_RBRACK, "]"); + total_offset += index.low * (unsigned long) (elem_size > 0 ? elem_size : (DATA_INT & 0x1f)); + + } + + if (pointer_depth > 0) { + current_object_size = elem_size > 0 ? elem_size : (DATA_PTR & 0x1f); + } else if (is_array && elem_size > 0) { + current_object_size = elem_size; + } else { + current_object_size = size; + } + + } + + expect (TOK_RPAREN, ")"); + + if (out) { + zext64 (out, total_offset); + } + + return 1; + +} + +int parse_constexpr_address_of_null_member (int64_s *out) { + + if (tok.kind != TOK_AMPER) { + return 0; + } + + get_token (); + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (parse_constexpr_null_member_address_after_lparen (out)) { + return 1; + } + + return 0; + +} + +int token_is_sizeof_keyword (void) { + return tok.kind == TOK_SIZEOF; +} + +static int64_s sizeof_from_current_token (void) { + + int64_s v; + int size; + + zext64 (&v, 0); + get_token (); + + size = parse_sizeof_value (); + zext64 (&v, (unsigned long) size); + + return v; + +} + +static int64_s const64_from_current_expr (void) { + + enum token_kind kill[6]; + int64_s v; + + kill[0] = TOK_SEMI; + kill[1] = TOK_COMMA; + kill[2] = TOK_RPAREN; + kill[3] = TOK_RBRACK; + kill[4] = TOK_RBRACE; + kill[5] = 0; + + v = expr_const64 (kill); + return v; + +} + +static int64_s const64_from_current_operand (void) { + + enum token_kind kill[26]; + + if (token_is_sizeof_keyword ()) { + return sizeof_from_current_token (); + } + + kill[0] = TOK_PLUS; + kill[1] = TOK_MINUS; + kill[2] = TOK_STAR; + kill[3] = TOK_BSLASH; + kill[4] = TOK_MOD; + kill[5] = TOK_LSH; + kill[6] = TOK_RSH; + kill[7] = TOK_AMPER; + kill[8] = TOK_PIPE; + kill[9] = TOK_CARET; + kill[10] = TOK_LESS; + kill[11] = TOK_LTEQ; + kill[12] = TOK_GREATER; + kill[13] = TOK_GTEQ; + kill[14] = TOK_EQEQ; + kill[15] = TOK_NOTEQ; + kill[16] = TOK_LOGAND; + kill[17] = TOK_LOGOR; + kill[18] = TOK_QMARK; + kill[19] = TOK_COLON; + kill[20] = TOK_SEMI; + kill[21] = TOK_COMMA; + kill[22] = TOK_RPAREN; + kill[23] = TOK_RBRACK; + kill[24] = TOK_RBRACE; + kill[25] = 0; + + return expr_const64 (kill); + +} + +static int64_s const64_from_current_foldable_expr (void) { + + enum token_kind kill[17]; + + if (token_is_sizeof_keyword ()) { + return sizeof_from_current_token (); + } + + kill[0] = TOK_LESS; + kill[1] = TOK_LTEQ; + kill[2] = TOK_GREATER; + kill[3] = TOK_GTEQ; + kill[4] = TOK_EQEQ; + kill[5] = TOK_NOTEQ; + kill[6] = TOK_LOGAND; + kill[7] = TOK_LOGOR; + kill[8] = TOK_QMARK; + kill[9] = TOK_COLON; + kill[10] = TOK_SEMI; + kill[11] = TOK_COMMA; + kill[12] = TOK_RPAREN; + kill[13] = TOK_RBRACK; + kill[14] = TOK_RBRACE; + kill[15] = 0; + kill[16] = 0; + + return expr_const64 (kill); + +} + +static long const_from_current_expr (void) { + + int64_s v = const64_from_current_expr (); + return (long) v.low; + +} + +static long const_from_current_case_expr (void) { + + enum token_kind kill[7]; + int64_s v; + + if (token_is_sizeof_keyword ()) { + + v = sizeof_from_current_token (); + return (long) v.low; + + } + + kill[0] = TOK_COLON; + kill[1] = TOK_SEMI; + kill[2] = TOK_COMMA; + kill[3] = TOK_RPAREN; + kill[4] = TOK_RBRACK; + kill[5] = TOK_RBRACE; + kill[6] = 0; + + v = expr_const64 (kill); + return (long) v.low; + +} + +static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count); + +static int declarator_pointed_size_now (void) { + + if (declarator_pointer_depth > 1) { + return DATA_PTR; + } + + if (parsed_type_is_aggregate) { + return parsed_type_size; + } + + return parsed_type_size & 0x1f; + +} + +static struct aggregate_tag_entry *hidden_typedef_pointer_entry_now (void) { + + if (declarator_pointer_depth != 0 || declarator_is_pointer || + declarator_has_array || declarator_has_function) { + return 0; + } + + if ((parsed_type_size & 0x1f) != (DATA_PTR & 0x1f) || + !parsed_type_tag_name[0]) { + return 0; + } + + return find_aggregate_tag (parsed_type_tag_name, 0); + +} + +static int declarator_effective_pointer_depth_now (void) { + + if (declarator_pointer_depth > 0) { + return declarator_pointer_depth; + } + + return hidden_typedef_pointer_entry_now () ? 1 : 0; + +} + +static int declarator_effective_pointed_size_now (int base_size, const int *field_sizes, int field_count) { + + struct aggregate_tag_entry *entry; + + if (declarator_pointer_depth > 0 || declarator_is_pointer) { + return declarator_pointed_size_now (); + } + + entry = hidden_typedef_pointer_entry_now (); + + if (entry) { + return entry->size; + } + + return declarator_element_size_from_fields (base_size, field_sizes, field_count); + +} + +static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count) { + + int total = 0; + int i; + + if (field_sizes && field_count > 0) { + + for (i = 0; i < field_count; i++) { + total += field_sizes[i] & 0x1f; + } + + if (total > 0) { + return total; + } + + } + + if (parsed_type_is_aggregate) { + return base_size; + } + + return base_size & 0x1f; + +} + +static int declarator_object_size (int base_size) { + + if (declarator_has_array) { + + int elem_size = declarator_is_pointer ? DATA_PTR : base_size; + + if (declarator_array_count <= 0) { + return elem_size; + } + + return elem_size * declarator_array_count; + + } + + if (declarator_is_pointer) { + return DATA_PTR; + } + + return base_size; + +} + +static int count_brace_initializer_elements_from_assign_now (void) { + + const char *p; + + int brace_depth = 0; + int paren_depth = 0; + int bracket_depth = 0; + int count = 0; + int saw_element = 0; + + if (tok.kind != TOK_ASSIGN || !tok.start) { + return 0; + } + + p = tok.start; + + while (*p && *p != '=') { + p++; + } + + if (*p != '=') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '{') { + return 0; + } + + p++; + brace_depth = 1; + + while (*p && brace_depth > 0) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + saw_element = 1; + + while (*p && *p != quote) { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p == quote) { + p++; + } + + continue; + + } + + if (*p == '{') { + + brace_depth++; + saw_element = 1; + + } else if (*p == '}') { + + if (brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) { + break; + } + + brace_depth--; + + } else if (*p == '(') { + + paren_depth++; + saw_element = 1; + + } else if (*p == ')') { + + if (paren_depth > 0) { + paren_depth--; + } + + } else if (*p == '[') { + + bracket_depth++; + saw_element = 1; + + } else if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + } else if (*p == ',' && brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) { + + if (saw_element) { + + count++; + saw_element = 0; + + } + + } else if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + saw_element = 1; + } + + p++; + + } + + if (saw_element) { + count++; + } + + return count; + +} + +static int parse_sizeof_member_expr_size (int leading_stars, int *out_size) { + + int size = DATA_INT & 0x1f; + int pointer_depth = 0; + int pointed_size = 0; + int is_array = 0; + int final_pointer_depth = 0; + int final_pointed_size = 0; + int final_is_array = 0; + + struct local_symbol *local; + + if (tok.kind != TOK_IDENT) { + return 0; + } + + local = find_local_symbol (tok.ident); + + if (local) { + + size = local->size; + + pointer_depth = local->pointer_depth; + pointed_size = local->pointed_size; + + is_array = local->is_array; + + } else if (find_global_symbol (tok.ident) >= 0) { + + size = get_global_symbol_size (tok.ident); + + pointer_depth = get_global_symbol_pointer_depth (tok.ident); + pointed_size = get_global_symbol_pointed_size (tok.ident); + + is_array = get_global_symbol_array (tok.ident); + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", tok.ident ? tok.ident : ""); + } + + final_pointer_depth = pointer_depth; + final_pointed_size = pointed_size; + final_is_array = is_array; + + get_token (); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + int member_pointer_depth = 0; + int member_pointed_size = 0; + int member_offset = 0; + int member_size = 0; + int member_is_array = 0; + + const char *member_tag_name = 0; + enum token_kind member_op = tok.kind; + + get_token (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, + "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + break; + + } + + if (find_member_info_ex (tok.ident, &member_offset, &member_size, + &member_pointed_size, &member_pointer_depth, + &member_is_array, 0)) { + + member_tag_name = last_found_member_tag_name ? last_found_member_tag_name : find_member_tag_name (tok.ident); + size = member_size; + + final_pointer_depth = member_pointer_depth; + final_pointed_size = member_pointed_size; + + final_is_array = member_is_array; + + if (final_pointer_depth > 0 && member_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0); + + if (entry) { + final_pointed_size = entry->size; + } + + } + + } else { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, + "unknown member '%s'", tok.ident ? tok.ident : ""); + + } + + get_token (); + + } + + while (tok.kind == TOK_LBRACK) { + + int depth = 1; + get_token (); + + while (tok.kind != TOK_EOF && depth > 0) { + + if (tok.kind == TOK_LBRACK) { + depth++; + } else if (tok.kind == TOK_RBRACK) { + + depth--; + + if (depth == 0) { + break; + } + + } + + get_token (); + + } + + if (tok.kind == TOK_RBRACK) { + get_token (); + } + + if (final_pointer_depth > 0) { + + size = final_pointer_depth > 1 ? DATA_PTR : final_pointed_size; + final_pointer_depth--; + + } else if (final_is_array && final_pointed_size > 0) { + size = final_pointed_size; + } + + final_is_array = 0; + + } + + if (leading_stars > 0 && final_is_array && final_pointer_depth == 0) { + + /* + * In expressions, an array object decays to a pointer to its first + * element. sizeof(*array) must therefore return the element size, + * not the total array object size. This matters for tables such as + * static const struct builtin_macro builtin[] where sizeof(*builtin) + * is used to compute the element count. + */ + if (final_pointed_size > 0) { + size = final_pointed_size; + } + + } else if (leading_stars > 0 && final_pointer_depth >= leading_stars) { + + int remaining_depth = final_pointer_depth - leading_stars; + size = remaining_depth > 0 ? DATA_PTR : final_pointed_size; + + } + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + *out_size = size; + return 1; + +} + +int parse_sizeof_value (void) { + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_inline = parsed_type_is_inline; + int saved_field_count = parsed_field_count; + int saved_fields[MAX_AGG_FIELDS]; + + int saved_declarator_is_pointer = declarator_is_pointer; + int saved_declarator_has_array = declarator_has_array; + int saved_declarator_has_function = declarator_has_function; + int saved_declarator_array_unsized = declarator_array_unsized; + long saved_declarator_array_count = declarator_array_count; + + int size = DATA_INT & 0x1f; + int i; + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + if (_accept (TOK_LPAREN)) { + + if (is_type_start (tok.kind)) { + + char *name = 0; + int base_size; + + parse_type_spec (); + base_size = parsed_type_size; + + /* + * sizeof(type-name) may not contain a declarator, for example + * sizeof(FILE). parse_declarator() normally clears these flags, + * but when the next token is ')' it is not called. Do not let a + * previous declaration such as FILE **__gtin(void) make this + * type-name look like a pointer and collapse the size to 4. + */ + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_function_is_pointer = 0; + declarator_function_param_count = 0; + declarator_function_has_prototype = 0; + declarator_function_is_variadic = 0; + declarator_array_unsized = 0; + declarator_array_count = 1; + declarator_last_array_count = 1; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&name); + } + + size = declarator_object_size (base_size); + + if (name) { + free (name); + } + + expect (TOK_RPAREN, ")"); + + } else if (is_string_token ()) { + + int64_s values[MAX_STRING_INIT_BYTES]; + int value_count = 0; + + parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); + size = value_count; + expect (TOK_RPAREN, ")"); + + } else { + + int depth = 1; + int first = 1; + int leading_stars = 0; + + while (tok.kind != TOK_EOF && depth > 0) { + + if (first) { + + while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + if (tok.kind == TOK_STAR) { + leading_stars++; + } + + get_token (); + + } + + } + + if (first && tok.kind == TOK_IDENT) { + + parse_sizeof_member_expr_size (leading_stars, &size); + + first = 0; + continue; + + } + + if (first && tok.kind == TOK_LPAREN && leading_stars > 0) { + + get_token (); + + if (tok.kind == TOK_IDENT) { + parse_sizeof_member_expr_size (leading_stars, &size); + } + + while (tok.kind != TOK_EOF && tok.kind != TOK_RPAREN) { + get_token (); + } + + if (tok.kind == TOK_RPAREN) { + get_token (); + } + + first = 0; + continue; + + } + + first = 0; + + if (tok.kind == TOK_LPAREN) { + depth++; + } else if (tok.kind == TOK_RPAREN) { + + depth--; + + if (depth == 0) { + break; + } + + } + + get_token (); + + } + + expect (TOK_RPAREN, ")"); + + } + + } else { + + int leading_stars = 0; + + while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + if (tok.kind == TOK_STAR) { + leading_stars++; + } + + get_token (); + + } + + if (is_string_token ()) { + + int64_s values[MAX_STRING_INIT_BYTES]; + int value_count = 0; + + parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); + size = value_count; + + } else if (tok.kind == TOK_IDENT) { + parse_sizeof_member_expr_size (leading_stars, &size); + } else if (tok.kind == TOK_LPAREN) { + + int depth = 1; + + get_token (); + + while (tok.kind != TOK_EOF && depth > 0) { + + if (tok.kind == TOK_LPAREN) { + depth++; + } else if (tok.kind == TOK_RPAREN) { + + depth--; + + if (depth == 0) { + break; + } + + } + + get_token (); + + } + + expect (TOK_RPAREN, ")"); + + } else if (tok.kind != TOK_EOF) { + get_token (); + } + + while (tok.kind == TOK_LBRACK || tok.kind == TOK_LPAREN) { + + enum token_kind close = (tok.kind == TOK_LBRACK) ? TOK_RBRACK : TOK_RPAREN; + int depth = 1; + + get_token (); + + while (tok.kind != TOK_EOF && depth > 0) { + + if ((close == TOK_RBRACK && tok.kind == TOK_LBRACK) || (close == TOK_RPAREN && tok.kind == TOK_LPAREN)) { + depth++; + } else if (tok.kind == close) { + + depth--; + + if (depth == 0) { + break; + } + + } + + get_token (); + + } + + expect (close, (close == TOK_RBRACK) ? "]" : ")"); + + } + + } + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + clear_parsed_fields (); + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + append_parsed_field (saved_fields[i]); + } + + declarator_is_pointer = saved_declarator_is_pointer; + declarator_has_array = saved_declarator_has_array; + declarator_has_function = saved_declarator_has_function; + declarator_array_unsized = saved_declarator_array_unsized; + declarator_array_count = saved_declarator_array_count; + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + return size; + +} + +static void make_declarator_fields (int *out_fields, int *out_count, const int *base_fields, int base_count, int base_size, int base_is_aggregate) { + + int i; + long r; + + *out_count = 0; + + if (declarator_is_pointer) { + + out_fields[(*out_count)++] = DATA_PTR; + return; + + } + + if (declarator_has_array) { + + if (base_is_aggregate && base_count > 0) { + + for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) { + + for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) { + out_fields[(*out_count)++] = base_fields[i]; + } + + } + + } else { + + for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) { + out_fields[(*out_count)++] = (base_size & 0x1f); + } + + } + + return; + + } + + for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) { + out_fields[(*out_count)++] = base_fields[i]; + } + + if (*out_count == 0) { + out_fields[(*out_count)++] = (base_size & 0x1f); + } + +} + +static void parse_statement (void); + +static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned); +static void emit_load_assignment_rhs_expression_to_reg (const char *reg); + +static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret); + +static int initializer_contains_runtime_call_now (void) { + + const char *p = tok.start; + + int paren_depth = 0; + int bracket_depth = 0; + int brace_depth = 0; + + while (*p) { + + if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) { + break; + } + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + const char *q = p + 1; + + while ((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') { + q++; + } + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + if (*q == '(') { + return 1; + } + + p = q; + continue; + + } + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p && *p != quote) { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p == quote) { + p++; + } + + continue; + + } + + if (*p == '(') { + paren_depth++; + } else if (*p == ')') { + + if (paren_depth > 0) { + paren_depth--; + } + + } else if (*p == '[') { + bracket_depth++; + } else if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + } else if (*p == '{') { + brace_depth++; + } else if (*p == '}') { + + if (brace_depth > 0) { + brace_depth--; + } + + } + + p++; + + } + + return 0; + +} + +static int auto_init_ident_char_now (int ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_'; +} + +static int auto_init_expr_continues_after_ident_now (void) { + + const char *p; + const char *q; + + if (tok.kind != TOK_IDENT) { + return 1; + } + + p = tok.start; + q = p; + + while (auto_init_ident_char_now ((unsigned char) *q)) { + q++; + } + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + return !(*q == ';' || *q == ',' || *q == '}'); + +} + +static int auto_initializer_needs_runtime_now (void) { + + const char *p = tok.start; + int paren_depth = 0; + int bracket_depth = 0; + int brace_depth = 0; + + if (initializer_contains_runtime_call_now () || tok.kind == TOK_LPAREN) { + return 1; + } + + if (tok.kind == TOK_IDENT) { + return auto_init_expr_continues_after_ident_now (); + } + + if (tok.kind == TOK_AMPER) { + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 1; + } + + while (auto_init_ident_char_now ((unsigned char) *p)) { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return !(*p == ';' || *p == ',' || *p == '}'); + + } + + p = tok.start; + + while (*p) { + + if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) { + break; + } + + if (*p == '[' || *p == ']' || *p == '*') { + return 1; + } + + if (*p == '(') { + paren_depth++; + } else if (*p == ')') { + if (paren_depth > 0) paren_depth--; + } else if (*p == '[') { + bracket_depth++; + } else if (*p == ']') { + if (bracket_depth > 0) bracket_depth--; + } else if (*p == '{') { + brace_depth++; + } else if (*p == '}') { + if (brace_depth > 0) brace_depth--; + } + + if (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') && p[0] != 'L') { + return 1; + } + + p++; + + } + + return 0; + +} + +static void parse_local_initializer_value (struct local_init *init) { + + init->kind = LOCAL_INIT_CONST; + init->symbol = 0; + init->value.low = 0; + init->value.high = 0; + init->source_offset = 0; + init->source_size = 0; + + if (tok.kind == TOK_LBRACE) { + + get_token (); + parse_local_initializer_value (init); + + while (_accept (TOK_COMMA)) { + skip_initializer (); + } + + expect (TOK_RBRACE, "}"); + return; + + } + + if (tok.kind == TOK_AMPER) { + + get_token (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'"); + return; + + } + + init->kind = LOCAL_INIT_ADDRESS; + init->symbol = xstrdup (tok.ident); + + get_token (); + return; + + } + + if (tok.kind == TOK_IDENT) { + + struct local_symbol *src = find_local_symbol (tok.ident); + + if (src) { + + if (src->is_static && src->static_label) { + + init->kind = LOCAL_INIT_GLOBAL; + init->symbol = xstrdup (src->static_label); + + } else { + + init->kind = LOCAL_INIT_STACK; + + init->source_offset = src->offset; + init->source_size = src->size; + + } + + get_token (); + return; + + } + + if (find_global_symbol (tok.ident) >= 0) { + + init->kind = LOCAL_INIT_GLOBAL; + init->symbol = xstrdup (tok.ident); + + get_token (); + return; + + } + + } + + init->value = const64_from_current_expr (); + +} + +static void parse_local_string_field_initializer (struct local_init *inits, int *init_count, int max_inits, long base_offset, int field_size) { + + int64_s values[MAX_STRING_INIT_BYTES]; + int value_count = 0, i; + + parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); + + for (i = 0; i < field_size; i++) { + + if (*init_count >= max_inits) { + continue; + } + + inits[*init_count].offset = base_offset + i; + inits[*init_count].size = 1; + inits[*init_count].kind = LOCAL_INIT_CONST; + inits[*init_count].symbol = 0; + inits[*init_count].value.low = (i < value_count) ? (values[i].low & 0xffUL) : 0; + inits[*init_count].value.high = 0; + inits[*init_count].source_offset = 0; + inits[*init_count].source_size = 0; + + (*init_count)++; + + } + +} + +static void parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) { + + int field_index = 0; + long field_offset = 0; + int braced = 0; + + if (tok.kind == TOK_LBRACE) { + braced = 1; + get_token (); + } + + while (field_index < field_count) { + + int field_size = fields[field_index]; + + if (field_size < 0) { + + field_offset += -field_size; + + field_index++; + continue; + + } + + if (braced && tok.kind == TOK_RBRACE) { + + if (*init_count < max_inits) { + + inits[*init_count].offset = base_offset + field_offset; + inits[*init_count].size = field_size; + inits[*init_count].kind = LOCAL_INIT_CONST; + inits[*init_count].symbol = 0; + inits[*init_count].value.low = 0; + inits[*init_count].value.high = 0; + inits[*init_count].source_offset = 0; + inits[*init_count].source_size = 0; + + (*init_count)++; + + } + + field_offset += field_size; + field_index++; + + continue; + + } + + if (*init_count < max_inits) { + + if (is_string_token ()) { + parse_local_string_field_initializer (inits, init_count, max_inits, base_offset + field_offset, field_size); + } else { + + inits[*init_count].offset = base_offset + field_offset; + inits[*init_count].size = field_size; + + parse_local_initializer_value (&inits[*init_count]); + (*init_count)++; + + } + + } else { + skip_initializer (); + } + + field_offset += field_size; + field_index++; + + if (braced) { + + if (!_accept (TOK_COMMA)) { + break; + } + + if (tok.kind == TOK_RBRACE) { + break; + } + + } else { + break; + } + + } + + if (braced) { + + while (field_index < field_count) { + + int field_size = fields[field_index]; + + if (field_size < 0) { + + field_offset += -field_size; + + field_index++; + continue; + + } + + if (*init_count < max_inits) { + + inits[*init_count].offset = base_offset + field_offset; + inits[*init_count].size = field_size; + inits[*init_count].kind = LOCAL_INIT_CONST; + inits[*init_count].symbol = 0; + inits[*init_count].value.low = 0; + inits[*init_count].value.high = 0; + inits[*init_count].source_offset = 0; + inits[*init_count].source_size = 0; + + (*init_count)++; + + } + + field_offset += field_size; + field_index++; + + } + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + skip_initializer (); + + if (!_accept (TOK_COMMA)) { + break; + } + } + + expect (TOK_RBRACE, "}"); + + } + +} + +static void make_local_static_label (char *buf, size_t bufsz, const char *name) { + + if (!buf || bufsz == 0) { + return; + } + + if (!name) { + name = "anon"; + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (buf, "LC%d_%s", local_static_id++, name); + } else { + sprintf (buf, ".LC%d_%s", local_static_id++, name); + } + +} + +static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static); +static void emit_extern_symbol (const char *name, int size, int is_function); +static void emit_extern_reference_symbol (const char *name, int size); + +static void emit_block_static_object (const char *label, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate) { + + emit_global_object (label, size, is_array, array_count, field_sizes, field_count, values, symbols, value_count, is_aggregate, 1); + + /** + * We may have switched to .data/.data? to emit the static object while + * parsing a function body. The following statements still belong in + * .code/.text. + */ + switch_section (SECTION_TEXT); + +} + +static char *emit_string_literal_global (void); +static int is_string_token (void); + +static void ensure_block_stack_allocated (long block_stack_start, long *block_stack_bytes, int *block_stack_emitted) { + + long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4); + + /* + * The parser can emit code while it is still inside a declaration list + * containing comma-separated declarators and runtime initializers. Keep + * one word of conservative headroom once a block frame exists, so a + * subsequently-discovered automatic slot in the same declaration phase is + * not allowed to overlap the caller frame before the next adjustment is + * emitted. This fixes cases like: + * + * cpp_mffc *old = reader->mffc; + * struct if_stack *ifs, *next_ifs; + * ... next_ifs = ifs->next; + * + * where generated code used -12(%ebp) after reserving only 8 bytes. + */ + if (needed_stack_bytes > 0) { + needed_stack_bytes = align_up_long (needed_stack_bytes + 4, 4); + } + + if (!block_stack_bytes || !block_stack_emitted) { + return; + } + + if (!*block_stack_emitted) { + + *block_stack_bytes = needed_stack_bytes; + emit_stack_adjust (*block_stack_bytes, 1); + + if (current_parse_block_depth > 1) { + current_block_cleanup_bytes += *block_stack_bytes; + } + + *block_stack_emitted = (*block_stack_bytes > 0); + + } else if (needed_stack_bytes > *block_stack_bytes) { + + long extra_stack_bytes = needed_stack_bytes - *block_stack_bytes; + emit_stack_adjust (extra_stack_bytes, 1); + + if (current_parse_block_depth > 1) { + current_block_cleanup_bytes += extra_stack_bytes; + } + + *block_stack_bytes = needed_stack_bytes; + + } + +} + +static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count); +static void parse_string_initializer_values (int64_s *values, int max_values, int *count); + +static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count); +static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count); + +static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width); +static int64_s parse_floating_const_expr_bits_now (int size); + +static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi); +static void emit_store_reg_to_local (long offset, int size, const char *reg); + +static void emit_load_local_address_to_reg_now (const char *reg, long offset); +static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size); + +static void parse_block (void) { + + int declaration_phase = 1, is_function_body = (current_parse_block_depth == 0); + + int block_local_start = local_symbol_count; + int block_stack_emitted = 0; + + long block_stack_start = current_local_stack_size; + long block_stack_bytes = 0; + + struct local_init inits[MAX_LOCAL_INITS]; + int block_scope_start, init_count = 0; + + block_scope_start = is_function_body ? 0 : block_local_start; + current_parse_block_depth++;; + + expect (TOK_LBRACE, "{"); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + if (is_type_start (tok.kind)) { + + if (!declaration_phase) { + + if (state->std == 90) { + report_line_at (get_filename (), get_line_number (), (state->pedantic ? REPORT_ERROR : REPORT_WARNING), tok.start, tok.caret, "ISO C90 forbids mixed declarations and code"); + } + + } + + parse_type_spec (); + + for (;;) { + + long object_offset = 0; + + int object_init_size = 0; + int object_is_auto = 0; + + char *name = 0; + unsigned long name_line; + + const char *name_start, *name_caret; + int object_align, object_size = 0; + + int object_fields[MAX_AGG_FIELDS]; + int object_field_count = 0; + int init_value_count = 0; + + int64_s init_values[MAX_AGG_FIELDS]; + int i; + + char *init_symbols[MAX_AGG_FIELDS]; + char static_label[128]; + + for (i = 0; i < MAX_AGG_FIELDS; i++) { + init_symbols[i] = 0; + } + + parse_declarator (&name); + apply_typedef_array_to_declarator (); + + if (declarator_has_array && declarator_array_unsized && tok.kind == TOK_ASSIGN) { + + int inferred_count = count_brace_initializer_elements_from_assign_now (); + + if (inferred_count > 0) { + declarator_array_count = inferred_count; + } + + } + + name_line = last_declarator_name_line; + name_start = last_declarator_name_start; + name_caret = last_declarator_name_caret; + + make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate); + + if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : ""); + } + + if (declarator_is_pointer) { + + object_field_count = 1; + object_fields[0] = DATA_PTR; + + } + + if (parsed_storage_class == STORAGE_TYPEDEF && name) { + + save_typedef_name (name, declarator_object_size (parsed_type_size), + (declarator_is_pointer ? 0 : parsed_type_is_unsigned), + (declarator_is_pointer ? 0 : parsed_type_is_void), + (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)), + (!declarator_is_pointer && declarator_has_array), + declarator_array_count, parsed_type_size, + object_fields, object_field_count); + + } else if (name && parsed_storage_class == STORAGE_EXTERN) { + + if (add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) { + + set_global_symbol_size (name, (declarator_has_function || declarator_is_pointer) ? DATA_PTR : declarator_object_size (parsed_type_size)); + set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + set_global_symbol_tag_name (name, parsed_type_tag_name); + set_global_symbol_unsigned (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned); + set_global_symbol_floating (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating); + set_global_symbol_returns_void (name, declarator_has_function && parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer); + + if (declarator_has_function) { + set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic); + } + + switch_section (SECTION_TEXT); + + } + + } else if (name && parsed_storage_class != STORAGE_STATIC) { + + object_size = declarator_object_size (parsed_type_size); + object_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (parsed_type_is_aggregate ? DATA_PTR : parsed_type_size); + + object_offset = add_local_symbol (name, object_size, object_align, + (declarator_is_pointer ? 0 : parsed_type_is_unsigned), + block_scope_start, name_line, name_start, name_caret); + + /* + * Reserve stack space as soon as an automatic local receives + * an EBP-relative slot. Some blocks start with declarations + * and then immediately execute statements that write those + * locals, for example: + * + * if (...) { location_t loc; unsigned int i; loc.file = ...; } + * + * Waiting until the declaration phase is exited proved too + * fragile after runtime initializer handling was added: code + * could use -4/-8/-12(%ebp) before any sub esp had been + * emitted. Keep block_stack_bytes in sync here so the later + * declaration-phase transition is a no-op unless a later + * unsized aggregate expands the frame. + */ + ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted); + + set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating); + set_local_symbol_array (name, declarator_has_array); + set_local_symbol_array_element_size (name, declarator_has_array ? (int)(object_size / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0); + set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + + if (!declarator_is_pointer && parsed_type_tag_name[0]) { + + struct local_symbol *lsym = find_local_symbol (name); + + if (lsym) { + lsym->tag_name = xstrdup (parsed_type_tag_name); + } + + } + + object_init_size = declarator_is_pointer ? DATA_PTR : parsed_type_size; + object_is_auto = 1; + + /* + * If an earlier automatic declaration in this block had a + * runtime initializer, stack space has already been emitted. + * Any later automatic declaration extends current_local_stack_size, + * and its initializer may immediately store to the new slot. + * Reserve the extra bytes before parsing/emitting that initializer; + * otherwise code can write to -8(%ebp), -12(%ebp), etc. after + * only the first 4 bytes were allocated. + */ + if (block_stack_emitted) { + ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted); + } + + } + + if (_accept (TOK_ASSIGN)) { + + /* + * Reserve the stack slot before parsing an automatic initializer. + * + * Some initializer forms which are not compile-time constants + * can still reach code emission through parse_local_initializer_value(). + * If the stack adjustment is delayed until the first following + * statement, those stores use offsets that have not yet been + * reserved. In create_definition(), this affected: + * + * cpp_token *saved_cur_token = reader->cur_token; + * + * The generated code stored to -64(%ebp) while only -60 bytes + * had been allocated, so the following call overwrote the saved + * value and reader->cur_token was restored with garbage. + */ + if (object_is_auto) { + ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted); + } + + if (parsed_storage_class == STORAGE_EXTERN) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "extern declaration '%s' is initialized", name ? name : ""); + skip_initializer (); + + } else if (parsed_storage_class == STORAGE_STATIC && name) { + + if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) { + + if (is_string_token ()) { + parse_string_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count); + } else { + parse_char_array_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count, declarator_last_array_count); + } + + if (declarator_array_unsized) { + declarator_array_count = init_value_count; + } + + } else if (declarator_is_pointer && is_string_token ()) { + + init_values[0].low = 0; + init_values[0].high = 0; + + init_symbols[0] = emit_string_literal_global (); + init_value_count = 1; + + } else if (!declarator_is_pointer && parsed_type_is_floating) { + + init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size); + init_symbols[0] = 0; + init_value_count = 1; + + } else { + + { + + int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses; + global_initializer_accept_symbol_addresses = declarator_is_pointer || parsed_type_is_aggregate || declarator_has_array; + + if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) { + parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count)); + } else { + parse_global_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count); + } + + global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses; + + } + + if (declarator_has_array && declarator_array_unsized) { + + if (parsed_type_is_aggregate && object_field_count > 0) { + declarator_array_count = (init_value_count + object_field_count - 1) / object_field_count; + } else { + declarator_array_count = init_value_count; + } + + } + + } + + } else if (object_is_auto && declarator_has_array && declarator_is_pointer && tok.kind == TOK_LBRACE) { + + int elem_index = 0; + int elem_size = DATA_PTR; + int first_init_index = init_count; + + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + if (init_count < MAX_LOCAL_INITS) { + + inits[init_count].offset = object_offset + (elem_index * elem_size); + inits[init_count].size = elem_size; + + if (is_string_token ()) { + + inits[init_count].kind = LOCAL_INIT_ADDRESS; + inits[init_count].symbol = emit_string_literal_global (); + inits[init_count].value.low = 0; + inits[init_count].value.high = 0; + inits[init_count].source_offset = 0; + inits[init_count].source_size = 0; + + switch_section (SECTION_TEXT); + + } else { + parse_local_initializer_value (&inits[init_count]); + } + + init_count++; + + } else { + skip_initializer (); + } + + elem_index++; + + if (!_accept (TOK_COMMA)) { + break; + } + + if (tok.kind == TOK_RBRACE) { + break; + } + + } + + if (declarator_array_unsized && elem_index > 0 && (elem_index * elem_size) > object_size) { + + long old_offset = object_offset; + long new_offset; + long delta; + int adj_i; + + current_local_stack_size += (elem_index * elem_size) - object_size; + new_offset = -current_local_stack_size; + delta = new_offset - old_offset; + object_offset = new_offset; + object_size = elem_index * elem_size; + + if (local_symbol_count > 0 && name && local_symbols[local_symbol_count - 1].name + && strcmp (local_symbols[local_symbol_count - 1].name, name) == 0) { + + local_symbols[local_symbol_count - 1].offset = object_offset; + local_symbols[local_symbol_count - 1].size = object_size; + + } + + for (adj_i = first_init_index; adj_i < init_count; adj_i++) { + inits[adj_i].offset += delta; + } + + } + + expect (TOK_RBRACE, "}"); + + } else if (object_is_auto && !declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array) && tok.kind == TOK_LBRACE) { + parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count); + } else if (object_is_auto && auto_initializer_needs_runtime_now ()) { + + long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4); + + if (!block_stack_emitted) { + + block_stack_bytes = needed_stack_bytes; + emit_stack_adjust (block_stack_bytes, 1); + + if (!is_function_body) { + current_block_cleanup_bytes += block_stack_bytes; + } + + block_stack_emitted = (block_stack_bytes > 0); + + } else if (needed_stack_bytes > block_stack_bytes) { + + long extra_stack_bytes = needed_stack_bytes - block_stack_bytes; + emit_stack_adjust (extra_stack_bytes, 1); + + if (!is_function_body) { + current_block_cleanup_bytes += extra_stack_bytes; + } + + block_stack_bytes = needed_stack_bytes; + + } + + emit_local_initializers (inits, init_count); + init_count = 0; + + if (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)) { + + emit_load_local_address_to_reg_now ("edx", object_offset); + + if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, object_size)) { + + if (tok.kind == TOK_IDENT && token_identifier_is_function_call_rhs_now () && + get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION && + get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) { + + pending_struct_return_lhs = find_local_symbol (name); + pending_struct_return_global_name = 0; + + emit_load_assignment_rhs_expression_to_reg ("eax"); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_store_reg_to_local (object_offset, object_init_size, "eax"); + + } + + } + + } else if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_is_pointer) { + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", parsed_type_is_unsigned); + emit_store_pair_to_local64 (object_offset, "eax", "edx"); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_store_reg_to_local (object_offset, object_init_size, "eax"); + + } + + } else if (object_is_auto && init_count < MAX_LOCAL_INITS) { + + inits[init_count].offset = object_offset; + inits[init_count].size = object_init_size; + + if (declarator_is_pointer && is_string_token ()) { + + inits[init_count].kind = LOCAL_INIT_ADDRESS; + inits[init_count].symbol = emit_string_literal_global (); + inits[init_count].value.low = 0; + inits[init_count].value.high = 0; + inits[init_count].source_offset = 0; + inits[init_count].source_size = 0; + + switch_section (SECTION_TEXT); + + } else { + parse_local_initializer_value (&inits[init_count]); + } + + init_count++; + + } else { + skip_initializer (); + } + + } + + if (name && parsed_storage_class == STORAGE_STATIC) { + + make_local_static_label (static_label, sizeof (static_label), name); + + if (add_global_symbol (static_label, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) { + + set_global_symbol_size (static_label, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size)); + set_global_symbol_pointer_info (static_label, declarator_pointer_depth, + declarator_is_pointer ? declarator_pointed_size_now () : + declarator_element_size_from_fields (parsed_type_size, object_fields, object_field_count)); + set_global_symbol_unsigned (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned); + set_global_symbol_floating (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating); + set_global_symbol_array (static_label, declarator_has_array); + set_global_symbol_array_count (static_label, declarator_has_array ? declarator_array_count : 0); + set_global_symbol_array_element_size (static_label, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0); + + emit_block_static_object (static_label, + declarator_is_pointer ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f)), + declarator_has_array, declarator_array_count, + object_fields, object_field_count, + init_values, init_symbols, init_value_count, + (!declarator_is_pointer && parsed_type_is_aggregate)); + + } + + add_static_local_symbol (name, static_label, + declarator_object_size (parsed_type_size), + type_alignment (declarator_is_pointer ? DATA_PTR : parsed_type_size), + (declarator_is_pointer ? 0 : parsed_type_is_unsigned), + block_scope_start, name_line, name_start, name_caret); + + set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating); + set_local_symbol_array (name, declarator_has_array); + set_local_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0); + set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + + } + + for (i = 0; i < MAX_AGG_FIELDS; i++) { + + if (init_symbols[i]) { + + free (init_symbols[i]); + init_symbols[i] = 0; + + } + + } + + if (name) { + free (name); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_SEMI, ";"); + + if (block_stack_emitted) { + ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted); + } + + continue; + + } + + if (declaration_phase) { + + declaration_phase = 0; + + { + + long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4); + + if (!block_stack_emitted) { + + block_stack_bytes = needed_stack_bytes; + emit_stack_adjust (block_stack_bytes, 1); + + if (!is_function_body) { + current_block_cleanup_bytes += block_stack_bytes; + } + + block_stack_emitted = (block_stack_bytes > 0); + + } else if (needed_stack_bytes > block_stack_bytes) { + + long extra_stack_bytes = needed_stack_bytes - block_stack_bytes; + emit_stack_adjust (extra_stack_bytes, 1); + + if (!is_function_body) { + current_block_cleanup_bytes += extra_stack_bytes; + } + + block_stack_bytes = needed_stack_bytes; + + } + + } + + emit_local_initializers (inits, init_count); + init_count = 0; + + } + + parse_statement (); + + } + + if (declaration_phase) { + + /** + * The block contained declarations only. We still need to reserve + * stack space for those automatic objects, otherwise a function like + * + * void f(void) { int a; } + * + * would produce no `sub esp, ...` at all. + */ + declaration_phase = 0; + + { + + long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4); + + if (!block_stack_emitted) { + + block_stack_bytes = needed_stack_bytes; + emit_stack_adjust (block_stack_bytes, 1); + block_stack_emitted = (block_stack_bytes > 0); + + } else if (needed_stack_bytes > block_stack_bytes) { + + emit_stack_adjust (needed_stack_bytes - block_stack_bytes, 1); + block_stack_bytes = needed_stack_bytes; + + } + + } + + emit_local_initializers (inits, init_count); + init_count = 0; + + } + + expect (TOK_RBRACE, "}"); + + if (!is_function_body && block_stack_emitted && block_stack_bytes > 0) { + + emit_stack_adjust (block_stack_bytes, 0); + + if (current_block_cleanup_bytes >= block_stack_bytes) { + current_block_cleanup_bytes -= block_stack_bytes; + } else { + current_block_cleanup_bytes = 0; + } + + } + + current_parse_block_depth--; + truncate_local_symbols (block_local_start, block_stack_start); + +} + +static int is_assignment_operator (enum token_kind k) { + + switch (k) { + + case TOK_ASSIGN: + case TOK_PLUSEQ: + case TOK_MINUSEQ: + case TOK_STAREQ: + case TOK_SLASHEQ: + case TOK_MODEQ: + case TOK_ANDEQ: + case TOK_OREQ: + case TOK_XOREQ: + case TOK_LSHEQ: + case TOK_RSHEQ: + + return 1; + + default: + + return 0; + + } + +} + +static void emit_load_local_to_reg (const char *reg, long offset, int size) { + + char memref[64]; + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, byte %s\n" : " movzx %s, byte ptr %s\n"), reg, memref); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, word %s\n" : " movzx %s, word ptr %s\n"), reg, memref); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), reg, memref); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %ld(%%ebp), %%%s\n", offset, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %ld(%%ebp), %%%s\n", offset, reg); + } else { + fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, reg); + } + + } + +} + +static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) { + + const char *asm_symbol; + + if (!state->ofp || !reg || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, size); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, byte [%s]\n" : " movzx %s, byte ptr %s\n"), reg, asm_symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, word [%s]\n" : " movzx %s, word ptr %s\n"), reg, asm_symbol); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr %s\n"), reg, asm_symbol); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %s, %%%s\n", asm_symbol, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %s, %%%s\n", asm_symbol, reg); + } else { + fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg); + } + + } + +} + +static void emit_store_reg_to_local (long offset, int size, const char *reg) { + + char memref[64]; + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, %cl\n" : " mov byte ptr %s, %cl\n"), memref, reg[1]); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, %cx\n" : " mov word ptr %s, %cx\n"), memref, reg[1]); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref, reg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%cl, %ld(%%ebp)\n", reg[1], offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%cx, %ld(%%ebp)\n", reg[1], offset); + } else { + fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", reg, offset); + } + + } + +} + +static void emit_store_reg_to_global (const char *symbol, int size, const char *reg) { + + const char *asm_symbol; + + if (!state->ofp || !symbol || !reg) { + return; + } + + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte [%s], %cl\n" : " mov byte ptr %s, %cl\n"), asm_symbol, reg[1]); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word [%s], %cx\n" : " mov word ptr %s, %cx\n"), asm_symbol, reg[1]); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr %s, %s\n"), asm_symbol, reg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%cl, %s\n", reg[1], asm_symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%cx, %s\n", reg[1], asm_symbol); + } else { + fprintf (state->ofp, " movl %%%s, %s\n", reg, asm_symbol); + } + + } + +} + +static void emit_load_local64_to_pair (long offset, const char *lo, const char *hi) { + + char memref_lo[64], memref_hi[64]; + + if (!state->ofp || !lo || !hi) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset); + format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4); + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), lo, memref_lo); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), hi, memref_hi); + + } else { + + fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, lo); + fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset + 4, hi); + + } + +} + +static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi) { + + char memref_lo[64], memref_hi[64]; + + if (!state->ofp || !lo || !hi) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset); + format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4); + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref_lo, lo); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref_hi, hi); + + } else { + + fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", lo, offset); + fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", hi, offset + 4); + + } + +} + +static void emit_load_global64_to_pair (const char *lo, const char *hi, const char *symbol) { + + const char *asm_symbol; + + if (!state->ofp || !lo || !hi || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr %s\n"), lo, asm_symbol); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s + 4]\n" : " mov %s, dword ptr %s + 4\n"), hi, asm_symbol); + + } else { + + fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, lo); + fprintf (state->ofp, " movl %s+4, %%%s\n", asm_symbol, hi); + + } + +} + +static void emit_store_pair_to_global64 (const char *symbol, const char *lo, const char *hi) { + + const char *asm_symbol; + + if (!state->ofp || !symbol || !lo || !hi) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr %s, %s\n"), asm_symbol, lo); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s + 4], %s\n" : " mov dword ptr %s + 4, %s\n"), asm_symbol, hi); + + } else { + + fprintf (state->ofp, " movl %%%s, %s\n", lo, asm_symbol); + fprintf (state->ofp, " movl %%%s, %s+4\n", hi, asm_symbol); + + } + +} + +static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line); +static void emit_push_pending_struct_return_address_now (int stack_arg_bytes); + +static int emit_push_aggregate_from_addr_reg_now (const char *reg, int size) { + + int offset; + int chunk; + + if (!reg || size <= (DATA_PTR & 0x1f)) { + return 0; + } + + if (!state->ofp) { + return 1; + } + + for (offset = size; offset > 0; ) { + + if (offset >= 4) { + + chunk = 4; + offset -= 4; + + } else if (offset >= 2) { + + chunk = 2; + offset -= 2; + + } else { + + chunk = 1; + offset -= 1; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (chunk == 4) { + fprintf (state->ofp, " push dword [%s + %d]\n", reg, offset); + } else if (chunk == 2) { + + fprintf (state->ofp, " movzx edx, word [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push edx\n"); + + } else { + + fprintf (state->ofp, " movzx edx, byte [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push edx\n"); + + } + + } else { + + if (chunk == 4) { + fprintf (state->ofp, " push dword ptr [%s + %d]\n", reg, offset); + } else if (chunk == 2) { + + fprintf (state->ofp, " movzx edx, word ptr [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push edx\n"); + + } else { + + fprintf (state->ofp, " movzx edx, byte ptr [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push edx\n"); + + } + + } + + } else { + + if (chunk == 4) { + fprintf (state->ofp, " pushl %d(%%%s)\n", offset, reg); + } else if (chunk == 2) { + + fprintf (state->ofp, " movzwl %d(%%%s), %%edx\n", offset, reg); + fprintf (state->ofp, " pushl %%edx\n"); + + } else { + + fprintf (state->ofp, " movzbl %d(%%%s), %%edx\n", offset, reg); + fprintf (state->ofp, " pushl %%edx\n"); + + } + + } + + } + + return 1; + +} + +static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg); + +static int is_arithmetic_binary_operator (enum token_kind k) { + + return k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH || + k == TOK_MOD || k == TOK_AMPER || k == TOK_PIPE || k == TOK_CARET || + k == TOK_LSH || k == TOK_RSH; + +} + +static void emit_push_reg_now (const char *reg) { + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " push %s\n", reg); + } else { + fprintf (state->ofp, " pushl %%%s\n", reg); + } + +} + +static void emit_pop_reg_now (const char *reg) { + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " pop %s\n", reg); + } else { + fprintf (state->ofp, " popl %%%s\n", reg); + } + +} + +static void emit_mov_reg_to_reg_now (const char *dst, const char *src) { + + if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %s\n", dst, src); + } else { + fprintf (state->ofp, " movl %%%s, %%%s\n", src, dst); + } + +} + +static void emit_load_indexed_pointer_to_reg_now (const char *base_reg, const char *index_reg) { + + if (!state->ofp || !base_reg || !index_reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + %s * 4]\n", base_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + %s * 4]\n", base_reg, base_reg, index_reg); + } + + } else { + fprintf (state->ofp, " movl (%%%s,%%%s,4), %%%s\n", base_reg, index_reg, base_reg); + } + +} + +static void emit_load_indexed_char_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg) { + + if (!state->ofp || !base_reg || !index_reg || !dst_reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " movzx %s, byte [%s + %s]\n", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " movzx %s, byte ptr [%s + %s]\n", dst_reg, base_reg, index_reg); + } + + } else { + fprintf (state->ofp, " movzbl (%%%s,%%%s), %%%s\n", base_reg, index_reg, dst_reg); + } + +} + +static void emit_load_indexed_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { + + int scale = 1; + + const char *atype = "byte"; + const char *gasop = "movzbl"; + + if (!state->ofp || !base_reg || !index_reg || !dst_reg) { + return; + } + + elem_size &= 0x1f; + + if (elem_size == (DATA_SHORT & 0x1f)) { + + scale = 2; + + atype = "word"; + gasop = "movzwl"; + + } else if (elem_size == (DATA_INT & 0x1f) || elem_size == (DATA_LONG & 0x1f) || elem_size == DATA_PTR) { + + scale = 4; + + atype = "dword"; + gasop = "movl"; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (elem_size == (DATA_CHAR & 0x1f)) { + emit_load_indexed_char_to_reg_now (base_reg, index_reg, dst_reg); + } else if (scale == 1) { + + if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " movzx %s, word [%s + %s]\n", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " movzx %s, word ptr [%s + %s]\n", dst_reg, base_reg, index_reg); + } + + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, %s [%s + %s]\n", dst_reg, atype, base_reg, index_reg); + } else { + fprintf (state->ofp, " mov %s, %s ptr [%s + %s]\n", dst_reg, atype, base_reg, index_reg); + } + + } else { + + if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " movzx %s, word [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " movzx %s, word ptr [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale); + } + + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, %s [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " mov %s, %s ptr [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale); + } + + } + + } else { + + if (scale == 1) { + fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", gasop, base_reg, index_reg, dst_reg); + } else { + fprintf (state->ofp, " %s (%%%s,%%%s,%d), %%%s\n", gasop, base_reg, index_reg, scale, dst_reg); + } + + } + +} + +static void emit_load_symbol_address_to_reg_now (const char *reg, const char *symbol, long offset, int is_local) { + + const char *asm_symbol; + + if (!state->ofp || !reg) { + return; + } + + if (is_local) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " lea %s, [ebp%+ld]\n", reg, offset); + } else { + fprintf (state->ofp, " leal %ld(%%ebp), %%%s\n", offset, reg); + } + + return; + + } + + if (!symbol) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_PTR); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, %s\n", reg, asm_symbol); + } else { + fprintf (state->ofp, " mov %s, offset %s\n", reg, asm_symbol); + } + + } else { + fprintf (state->ofp, " movl $%s, %%%s\n", asm_symbol, reg); + } + +} + +static void emit_add_indexed_scaled_address_to_reg_now (const char *base_reg, const char *index_reg, int elem_size) { + + int raw_elem_size = elem_size; + int scale = 1; + + if (!state->ofp || !base_reg || !index_reg) { + return; + } + + if (raw_elem_size == DATA_SHORT || raw_elem_size == (DATA_SHORT & 0x1f)) { + + elem_size = DATA_SHORT & 0x1f; + scale = 2; + + } else if (raw_elem_size == DATA_INT || raw_elem_size == DATA_LONG || raw_elem_size == DATA_PTR || raw_elem_size == (DATA_INT & 0x1f)) { + + elem_size = DATA_INT & 0x1f; + scale = 4; + + } else if (elem_size > 1) { + scale = 0; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (scale == 0) { + + fprintf (state->ofp, " imul %s, %d\n", index_reg, elem_size); + fprintf (state->ofp, " lea %s, [%s + %s]\n", base_reg, base_reg, index_reg); + + } else if (scale == 1) { + fprintf (state->ofp, " lea %s, [%s + %s]\n", base_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " lea %s, [%s + %s * %d]\n", base_reg, base_reg, index_reg, scale); + } + + } else { + + if (scale == 0) { + + fprintf (state->ofp, " imull $%d, %%%s, %%%s\n", elem_size, index_reg, index_reg); + fprintf (state->ofp, " leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg); + + } else if (scale == 1) { + fprintf (state->ofp, " leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg); + } else { + fprintf (state->ofp, " leal (%%%s,%%%s,%d), %%%s\n", base_reg, index_reg, scale, base_reg); + } + + } + +} + +static int emit_parse_postfix_subscript_scaled_address_to_reg_now (const char *reg, int elem_size) { + + const char *index_reg; + int saw_subscript = 0; + + if (!reg) { + reg = "eax"; + } + + index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx"; + + while (tok.kind == TOK_LBRACK) { + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_assignment_rhs_expression_to_reg (index_reg); + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (tok.kind == TOK_LBRACK) { + emit_load_indexed_pointer_to_reg_now (reg, index_reg); + } else { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } + + } + + return saw_subscript; + +} + +static int emit_parse_postfix_subscripts_to_reg_now (const char *reg, int elem_size, int pointer_depth, int pointed_size) { + + const char *index_reg; + int saw_subscript = 0; + + if (!reg) { + reg = "eax"; + } + + index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx"; + + while (tok.kind == TOK_LBRACK) { + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_assignment_rhs_expression_to_reg (index_reg); + + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (tok.kind == TOK_LBRACK) { + + emit_load_indexed_pointer_to_reg_now (reg, index_reg); + + if (pointer_depth > 0) { + pointer_depth--; + } + + if (pointed_size > 0) { + elem_size = (pointer_depth > 1) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size); + } + + } else { + + if ((elem_size & 0x1f) > DATA_PTR) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else { + emit_load_indexed_sized_to_reg_now (reg, index_reg, reg, elem_size); + } + + } + + } + + return saw_subscript; + +} + +static int parse_incdec_identifier_now (enum token_kind *op, char **name, const char **name_start, const char **name_caret, unsigned long *name_line) { + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + *op = tok.kind; + get_token (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", *op == TOK_INCR ? "++" : "--"); + + *name = 0; + return 1; + } + + *name = xstrdup (tok.ident); + *name_start = tok.start; + *name_caret = tok.caret; + *name_line = get_line_number (); + + get_token (); + return 1; + +} + +static void emit_apply_postfix_member_access_to_reg_now (const char *reg); +static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op); +static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size); + +static struct token *clone_current_token_now (void); +static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size); + +static int source_starts_prefix_incdec_parenthesized_deref_at (const char *p) { + + if (!p) { + return 0; + } + + if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) { + return 0; + } + + p += 2; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == ')'; + +} + +static int emit_load_prefix_incdec_parenthesized_deref_to_reg_now (const char *reg) { + + enum token_kind op; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *sym; + + int global_index; + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int lvalue_size = DATA_INT & 0x1f; + int step = 1; + + const char *addr_reg; + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + if (!source_starts_prefix_incdec_parenthesized_deref_at (tok.caret)) { + return 0; + } + + op = tok.kind; + get_token (); + + expect (TOK_LPAREN, "("); + expect (TOK_STAR, "*"); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + pointer_depth = sym->pointer_depth; + pointed_size = sym->pointed_size; + + } else if (global_index >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name); + + free (name); + return 1; + + } + + if (pointed_size <= 0) { + pointed_size = DATA_INT & 0x1f; + } + + if (pointer_depth > 1) { + + lvalue_size = DATA_PTR & 0x1f; + step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size); + + } else { + + lvalue_size = pointed_size & 0x1f; + step = 1; + + } + + if (!reg) { + reg = "eax"; + } + + addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx"; + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (addr_reg, name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg); + } + + } + + emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size); + + if (pointer_depth > 1) { + set_rhs_last_pointer_info (pointer_depth - 1, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return 1; + +} + +static int emit_load_prefix_incdec_deref_to_reg_now (const char *reg) { + + enum token_kind op; + + struct token *saved_tok; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + + struct local_symbol *sym; + + int global_index; + int deref_size = DATA_INT & 0x1f; + + const char *addr_reg; + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + saved_tok = clone_current_token_now (); + op = tok.kind; + + get_token (); + + if (tok.kind != TOK_STAR) { + + unget_token (saved_tok); + return 0; + + } + + get_token (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *"); + + free (saved_tok->ident); + free ((char *) saved_tok->start); + free (saved_tok); + + return 1; + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + if (sym->pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) { + deref_size = sym->pointed_size & 0x1f; + } + + } else if (global_index >= 0) { + + if (get_global_symbol_pointer_depth (name) > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) { + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + } + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + free (saved_tok->ident); + + free ((char *) saved_tok->start); + free (saved_tok); + + return 1; + + } + + if (!reg) { + reg = "eax"; + } + + addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx"; + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (addr_reg, name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now (reg, addr_reg, 0, deref_size); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add %s, 1\n" : " sub %s, 1\n", reg); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $1, %%%s\n" : " subl $1, %%%s\n", reg); + } + + } + + emit_store_reg_to_deref_reg_now (addr_reg, reg, deref_size); + + free (name); + + free (saved_tok->ident); + free ((char *) saved_tok->start); + + free (saved_tok); + return 1; + +} + +static int emit_load_prefix_incdec_to_reg_now (const char *reg) { + + enum token_kind op; + char *name; + + const char *name_start, *name_caret; + unsigned long name_line; + + struct local_symbol *sym; + + if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) { + return 1; + } + + if (emit_load_prefix_incdec_deref_to_reg_now (reg)) { + return 1; + } + + if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) { + return 0; + } + + if (!name) { + return 1; + } + + sym = find_local_symbol (name); + emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret); + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (reg, sym->static_label, sym->size); + } else { + emit_load_local_to_reg (reg, sym->offset, sym->size); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg (reg, name, get_global_symbol_size (name)); + } + + free (name); + return 1; + +} + +static int emit_load_prefix_incdec_member_to_reg_now (const char *reg) { + + struct token *saved_tok; + + enum token_kind op; + enum token_kind member_op = TOK_EOF; + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int size = DATA_INT & 0x1f; + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + saved_tok = clone_current_token_now (); + get_token (); + + if (tok.kind == TOK_STAR) { + + unget_token (saved_tok); + return 0; + + } + + unget_token (saved_tok); + + if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) { + return 1; + } + + op = tok.kind; + get_token (); + + if (tok.kind != TOK_LPAREN) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *sym; + + int global_index; + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--"); + return 1; + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (tok.kind == TOK_ARROW) { + + member_op = tok.kind; + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (name); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &offset, &size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return 1; + + } + + free (member); + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (reg, sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg (reg, name, DATA_PTR); + } else { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + free (name); + goto emit_member_incdec; + + } + + emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret); + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (reg, sym->static_label, sym->size); + } else { + emit_load_local_to_reg (reg, sym->offset, sym->size); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg (reg, name, get_global_symbol_size (name)); + } + + free (name); + return 1; + + } + + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + expect (TOK_RPAREN, ")"); + + if (postfix_member_seen && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + emit_apply_postfix_member_incdec_now (reg, op); + emit_load_member_from_addr_reg_now (reg, "edx", postfix_member_offset, postfix_member_size); + + return 1; + + } + + if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member after %s", op == TOK_INCR ? "++" : "--"); + return 1; + + } + + member_op = tok.kind; + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &offset, &size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + return 1; + + } + + free (member); + +emit_member_incdec: + + if (!state->ofp) { + return 1; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *insn = op == TOK_INCR ? "inc" : "dec"; + const char *opsize = "dword"; + + if (size == (DATA_CHAR & 0x1f)) { + opsize = "byte"; + } else if (size == (DATA_SHORT & 0x1f)) { + opsize = "word"; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " %s %s [%s + %d]\n", insn, opsize, reg, offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", reg, reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " mov %s, dword [%s + %d]\n", reg, reg, offset); + } + + } else { + + fprintf (state->ofp, " %s %s ptr [%s + %d]\n", insn, opsize, reg, offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", reg, reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", reg, reg, offset); + } + + } + + } else { + + const char *suffix = "l"; + + if (size == (DATA_CHAR & 0x1f)) { + suffix = "b"; + } else if (size == (DATA_SHORT & 0x1f)) { + suffix = "w"; + } + + fprintf (state->ofp, " %s%s %d(%%%s)\n", op == TOK_INCR ? "inc" : "dec", suffix, offset, reg); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, reg); + } else { + fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg); + } + + } + + return 1; + +} + +static int emit_load_prefix_incdec_to_pair_now (const char *lo, const char *hi) { + + enum token_kind op; + char *name; + + const char *name_start, *name_caret; + unsigned long name_line; + + struct local_symbol *sym; + + if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) { + return 0; + } + + if (!name) { + return 1; + } + + sym = find_local_symbol (name); + emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret); + + if (sym) { + + if (sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) { + + if (sym->is_static && sym->static_label) { + emit_load_global64_to_pair (lo, hi, sym->static_label); + } else { + emit_load_local64_to_pair (sym->offset, lo, hi); + } + + } else { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (lo, sym->static_label, sym->size); + } else { + emit_load_local_to_reg (lo, sym->offset, sym->size); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + } + + } + + } + + } else if (find_global_symbol (name) >= 0) { + + if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) { + emit_load_global64_to_pair (lo, hi, name); + } else { + + emit_load_global_to_reg (lo, name, get_global_symbol_size (name)); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + } + + } + + } + + } + + free (name); + return 1; + +} + +static int expression_text_mentions_64bit_symbol (const char *p) { + + char name[256]; + int depth = 0; + int i; + + if (!p) { + return 0; + } + + while (*p) { + + if (*p == '(') { + + depth++; + p++; + + continue; + + } + + if (*p == ')') { + + if (depth <= 0) { + return 0; + } + + depth--; + p++; + + continue; + + } + + if (*p == ';' || *p == '{' || *p == '}') { + return 0; + } + + if ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))) { + + i = 0; + + while ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) || (*p >= '0' && *p <= '9')) { + + if (i + 1 < (int) sizeof (name)) { + name[i++] = *p; + } + + p++; + + } + + name[i] = 0; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '.' || (p[0] == '-' && p[1] == '>')) { + continue; + } + + { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym && sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) { + return 1; + } + + } + + if (find_global_symbol (name) >= 0 && get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) { + return 1; + } + + continue; + + } + + p++; + + } + + return 0; + +} + +static int current_expression_mentions_64bit_symbol_now (void) { + + if (expression_text_mentions_64bit_symbol (tok.start)) { + return 1; + } + + if (expression_text_mentions_64bit_symbol (tok.caret)) { + return 1; + } + + return 0; + +} + +static const char *reg8_name_for_32 (const char *reg) { + + if (strcmp (reg, "eax") == 0) { + return "al"; + } + + if (strcmp (reg, "ebx") == 0) { + return "bl"; + } + + if (strcmp (reg, "ecx") == 0) { + return "cl"; + } + + if (strcmp (reg, "edx") == 0) { + return "dl"; + } + + return "al"; + +} + +static const char *reg16_name_for_32 (const char *reg) { + + if (strcmp (reg, "eax") == 0) { + return "ax"; + } + + if (strcmp (reg, "ebx") == 0) { + return "bx"; + } + + if (strcmp (reg, "ecx") == 0) { + return "cx"; + } + + if (strcmp (reg, "edx") == 0) { + return "dx"; + } + + return "ax"; + +} + +static void emit_extend_pair_high_from_low (const char *lo, const char *hi, int size, int is_unsigned) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (is_unsigned) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, %s\n", lo, reg8_name_for_32 (lo)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, %s\n", lo, reg16_name_for_32 (lo)); + } + + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsx %s, %s\n", lo, reg8_name_for_32 (lo)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %s, %s\n", lo, reg16_name_for_32 (lo)); + } + + fprintf (state->ofp, " mov %s, %s\n", hi, lo); + fprintf (state->ofp, " sar %s, 31\n", hi); + + } + + } else { + + if (is_unsigned) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %%%s, %%%s\n", reg16_name_for_32 (lo), lo); + } + + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movswl %%%s, %%%s\n", reg16_name_for_32 (lo), lo); + } + + fprintf (state->ofp, " movl %%%s, %%%s\n", lo, hi); + fprintf (state->ofp, " sarl $31, %%%s\n", hi); + + } + + } + +} + +static void emit_apply_integer_cast_to_reg_now (const char *reg, int size, int is_unsigned) { + + if (!state->ofp) { + return; + } + + if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f)) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", reg, reg8_name_for_32 (reg)); + } else { + fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", reg, reg16_name_for_32 (reg)); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, is_unsigned ? " movzbl %%%s, %%%s\n" : " movsbl %%%s, %%%s\n", reg8_name_for_32 (reg), reg); + } else { + fprintf (state->ofp, is_unsigned ? " movzwl %%%s, %%%s\n" : " movswl %%%s, %%%s\n", reg16_name_for_32 (reg), reg); + } + + } + +} + +static int const_integer_expr_text_is_foldable_now (const char *p) { + + int depth = 0; + int saw_token = 0; + int ch; + char word[64]; + int i; + + if (!p) { + return 0; + } + + while (*p) { + + ch = (unsigned char) *p; + + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { + + p++; + continue; + + } + + if (depth == 0) { + + if (*p == ';' || *p == ',' || *p == '}' || *p == ']') { + break; + } + + /* + * This helper is only for folding a complete integer expression + * operand. Do not claim that a leading parenthesized constant is + * foldable when it is followed by a lower-precedence operator such + * as the '<' in: + * + * ((3) < (1 + n)) ? ... + * + * Returning true there makes the caller consume only '(3)' and then + * expect a closing parenthesis while the current token is '<'. + */ + if (*p == ':' || *p == '?') { + return 0; + } + + if (*p == '<' || *p == '>' || *p == '=' || (*p == '!' && p[1] == '=')) { + + if ((p[0] == '<' && p[1] == '<') || (p[0] == '>' && p[1] == '>')) { + + saw_token = 1; + + p += 2; + continue; + + } else { + return 0; + } + } + + if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) { + return 0; + } + + } + + if (*p == '(') { + + depth++; + + saw_token = 1; + p++; + + continue; + + } + + if (*p == ')') { + + if (depth == 0) { + break; + } + + depth--; + p++; + + continue; + + } + + if (*p >= '0' && *p <= '9') { + + saw_token = 1; + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + continue; + + } + + if (*p == '\'') { + + saw_token = 1; + p++; + + while (*p && *p != '\'') { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p == '\'') { + p++; + } + + continue; + + } + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + i = 0; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + + if (i + 1 < (int) sizeof (word)) { + word[i++] = *p; + } + + p++; + + } + + word[i] = 0; + + if (strcmp (word, "sizeof") != 0 && + strcmp (word, "char") != 0 && + strcmp (word, "short") != 0 && + strcmp (word, "int") != 0 && + strcmp (word, "long") != 0 && + strcmp (word, "signed") != 0 && + strcmp (word, "unsigned") != 0 && + strcmp (word, "void") != 0) { + return 0; + } + + saw_token = 1; + continue; + + } + + if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' || *p == '&' || *p == '|' || *p == '^' || *p == '~' || *p == '!' || *p == '<' || *p == '>') { + + saw_token = 1; + p++; + + continue; + + } + + return 0; + + } + + return saw_token; + +} + +static void emit_load_const64_to_pair_now (const char *lo, const char *hi, int64_s v) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK); + fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK); + + } else { + + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo); + fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi); + + } + +} + +static void emit_load_const32_to_reg_now (const char *reg, int64_s v) { + + flush_pending_statement_labels (); + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg); + } + +} + +static void emit_load_address_to_reg_now (const char *reg, const char *symbol) { + + const char *asm_symbol; + flush_pending_statement_labels (); + + if (!state->ofp || !symbol || !*symbol) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_PTR); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, %s\n", reg, asm_symbol); + } else { + fprintf (state->ofp, " mov %s, offset %s\n", reg, asm_symbol); + } + + } else { + fprintf (state->ofp, " movl $%s, %%%s\n", asm_symbol, reg); + } + +} + +static void emit_load_deref_reg_now (const char *reg, int size); + +static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg); +static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line); + +static void emit_load_postfix_lvalue_address_to_pair_now (const char *lo, const char *hi, int size) { + + if (!state->ofp) { + return; + } + + if (size > (DATA_PTR & 0x1f)) { + + emit_push_reg_now (lo); + + emit_load_deref_reg_now (lo, DATA_PTR & 0x1f); + emit_pop_reg_now (hi); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, hi); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, hi); + } + + } else { + fprintf (state->ofp, " movl 4(%%%s), %%%s\n", hi, hi); + } + + } else { + + emit_load_deref_reg_now (lo, size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + } + + } + +} + +static void emit_store_pair_to_deref_reg_now (const char *addr_reg, const char *lo, const char *hi) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [%s], %s\n", addr_reg, lo); + fprintf (state->ofp, " mov dword [%s + 4], %s\n", addr_reg, hi); + + } else { + + fprintf (state->ofp, " mov dword ptr [%s], %s\n", addr_reg, lo); + fprintf (state->ofp, " mov dword ptr [%s + 4], %s\n", addr_reg, hi); + + } + + } else { + + fprintf (state->ofp, " movl %%%s, (%%%s)\n", lo, addr_reg); + fprintf (state->ofp, " movl %%%s, 4(%%%s)\n", hi, addr_reg); + + } + +} + +static void emit_load_pair_from_deref_reg_now (const char *lo, const char *hi, const char *addr_reg) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov %s, dword [%s]\n", lo, addr_reg); + fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, addr_reg); + + } else { + + fprintf (state->ofp, " mov %s, dword ptr [%s]\n", lo, addr_reg); + fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, addr_reg); + + } + + } else { + + fprintf (state->ofp, " movl (%%%s), %%%s\n", addr_reg, lo); + fprintf (state->ofp, " movl 4(%%%s), %%%s\n", addr_reg, hi); + + } + +} + +static void emit_copy_reg_now (const char *dst, const char *src) { + + if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %s\n", dst, src); + } else { + fprintf (state->ofp, " movl %%%s, %%%s\n", src, dst); + } + +} + +static void emit_load_assignment_rhs_to_pair (const char *lo, const char *hi) { + + if (_accept (TOK_LPAREN)) { + + if (token_starts_type_name ()) { + + int cast_size = 0; + int cast_is_unsigned = 0; + int cast_is_pointer = 0; + + if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) { + + emit_load_assignment_rhs_to_pair (lo, hi); + + { + + int applied_postfix = 0; + + if (cast_is_pointer) { + set_rhs_last_pointer_info (1, cast_size > 0 ? cast_size : (DATA_INT & 0x1f)); + } else { + emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); + } + + postfix_member_seen = 0; + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + applied_postfix = 1; + emit_apply_postfix_member_access_to_reg_now (lo); + + } + + if (applied_postfix && postfix_member_seen) { + + if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) { + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned); + } + + } + + } + + return; + + } + + } + + if (const_integer_expr_text_is_foldable_now (tok.caret)) { + + int64_s v = const64_from_current_foldable_expr (); + expect (TOK_RPAREN, ")"); + + emit_load_const64_to_pair_now (lo, hi, v); + return; + + } + + if (emit_load_parenthesized_indirect_member_to_reg_now (lo)) { + + emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1); + return; + + } + + emit_load_assignment_rhs_expression_to_pair (lo, hi, 1); + expect (TOK_RPAREN, ")"); + + { + + int applied_postfix = 0; + postfix_member_seen = 0; + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + applied_postfix = 1; + emit_apply_postfix_member_access_to_reg_now (lo); + + } + + if (applied_postfix && postfix_member_seen) { + + if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) { + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned); + } + + } + + } + + return; + + } + + if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS || tok.kind == TOK_TILDE || tok.kind == TOK_XMARK) { + + enum token_kind unary_op = tok.kind; + get_token (); + + emit_load_assignment_rhs_to_pair (lo, hi); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (unary_op == TOK_MINUS) { + + fprintf (state->ofp, " not %s\n", lo); + fprintf (state->ofp, " not %s\n", hi); + + fprintf (state->ofp, " add %s, 1\n", lo); + fprintf (state->ofp, " adc %s, 0\n", hi); + + } else if (unary_op == TOK_TILDE) { + + fprintf (state->ofp, " not %s\n", lo); + fprintf (state->ofp, " not %s\n", hi); + + } else if (unary_op == TOK_XMARK) { + + fprintf (state->ofp, " or %s, %s\n", lo, hi); + fprintf (state->ofp, " setz al\n"); + fprintf (state->ofp, " movzx eax, al\n"); + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", lo); + } + + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } + + } else { + + if (unary_op == TOK_MINUS) { + + fprintf (state->ofp, " notl %%%s\n", lo); + fprintf (state->ofp, " notl %%%s\n", hi); + + fprintf (state->ofp, " addl $1, %%%s\n", lo); + fprintf (state->ofp, " adcl $0, %%%s\n", hi); + + } else if (unary_op == TOK_TILDE) { + + fprintf (state->ofp, " notl %%%s\n", lo); + fprintf (state->ofp, " notl %%%s\n", hi); + + } else if (unary_op == TOK_XMARK) { + + fprintf (state->ofp, " orl %%%s, %%%s\n", hi, lo); + fprintf (state->ofp, " setz %%al\n"); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", lo); + } + + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + + } + + } + + } + + return; + + } + + if (emit_load_prefix_incdec_to_pair_now (lo, hi)) { + return; + } + + if (tok.kind == TOK_STAR) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + + int deref_size = DATA_INT & 0x1f; + int deref_unsigned = 1; + + int postfix_incdec = 0; + + enum token_kind postfix_op = TOK_EOF; + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + int64_s zero; + + zero.low = 0; + zero.high = 0; + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + + emit_load_const64_to_pair_now (lo, hi, zero); + return; + + } + + name = xstrdup (tok.ident); + get_token (); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + if (src->pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (src->pointer_depth == 1 && src->pointed_size > 0) { + deref_size = src->pointed_size & 0x1f; + } + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (lo, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (lo, src->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + + if (get_global_symbol_pointer_depth (name) > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) { + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + } + + emit_load_global_to_reg (lo, name, DATA_PTR); + + } else { + + int64_s zero; + + zero.low = 0; + zero.high = 0; + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + emit_load_const64_to_pair_now (lo, hi, zero); + free (name); + + return; + + } + + if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_push_reg_now (lo); + + emit_load_deref_reg_now (lo, DATA_INT & 0x1f); + emit_pop_reg_now (hi); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, hi); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, hi); + } + + } else { + fprintf (state->ofp, " movl 4(%%%s), %%%s\n", hi, hi); + } + + } + + } else { + + emit_load_deref_reg_now (lo, deref_size); + emit_extend_pair_high_from_low (lo, hi, deref_size, deref_unsigned); + + } + + if (postfix_incdec) { + + emit_push_reg_now (lo); + emit_push_reg_now (hi); + + emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret); + + emit_pop_reg_now (hi); + emit_pop_reg_now (lo); + + } + + free (name); + return; + + } + + if (token_is_sizeof_keyword ()) { + + int64_s v = sizeof_from_current_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK); + fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK); + + } else { + + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo); + fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi); + + } + + } + + return; + + } + + if (tok.kind == TOK_IDENT) { + + enum token_kind postfix_op = TOK_EOF; + char *name = xstrdup (tok.ident); + + const char *name_start = tok.start, *name_caret = tok.caret; + unsigned long name_line = get_line_number (); + + int postfix_incdec = 0; + + struct local_symbol *src; + int64_s enum_value; + + get_token (); + + if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) { + + emit_load_const64_to_pair_now (lo, hi, enum_value); + + free (name); + return; + + } + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (name)) { + ensure_global_function_symbol (name, name_start, name_caret, name_line); + } + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name); + } + + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", lo); + } + + if (strcmp (hi, "edx") != 0) { + fprintf (state->ofp, " mov %s, edx\n", hi); + } + + } else { + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", lo); + } + + if (strcmp (hi, "edx") != 0) { + fprintf (state->ofp, " movl %%edx, %%%s\n", hi); + } + + } + + } + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + src = find_local_symbol (name); + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) { + + int source_size; + + if (src || find_global_symbol (name) >= 0) { + + if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) { + + if (src) { + + postfix_copy_lvalue_size = src->pointer_depth > 0 ? src->pointed_size : src->size; + postfix_copy_lvalue_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : src->tag_name; + + } else { + + postfix_copy_lvalue_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name); + postfix_copy_lvalue_tag_name = 0; + + } + + } else if (src) { + + postfix_copy_lvalue_size = src->size; + postfix_copy_lvalue_tag_name = 0; + + } else { + + postfix_copy_lvalue_size = get_global_symbol_size (name); + postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name); + + } + + if (!emit_parse_postfix_copy_source_address_now (lo, src, name, name_start, name_caret, name_line)) { + + free (name); + return; + + } + + source_size = postfix_copy_lvalue_size; + + if (source_size <= 0) { + source_size = DATA_INT & 0x1f; + } + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "edx"; + int lvalue_size = index_step_size (source_size); + + get_token (); + + if (assign_op == TOK_ASSIGN && lvalue_size == (DATA_LLONG & 0x1f)) { + + emit_push_reg_now (lo); + + emit_load_assignment_rhs_expression_to_pair (lo, hi, 1); + emit_pop_reg_now (addr_reg); + + emit_store_pair_to_deref_reg_now (addr_reg, lo, hi); + + free (name); + return; + + } + + if (assign_op == TOK_ASSIGN) { + + emit_push_reg_now (lo); + + emit_load_assignment_rhs_expression_to_reg (lo); + emit_extend_pair_high_from_low (lo, hi, lvalue_size, 1); + + emit_pop_reg_now (addr_reg); + emit_store_reg_to_deref_reg_now (addr_reg, lo, lvalue_size); + + free (name); + return; + + } + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "compound member assignment expression not implemented in 64-bit context"); + + free (name); + return; + + } + + emit_load_postfix_lvalue_address_to_pair_now (lo, hi, source_size); + + free (name); + return; + + } + + } + + if (src) { + + if (src->size == (DATA_LLONG & 0x1f) && !src->is_floating) { + + if (src->is_static && src->static_label) { + emit_load_global64_to_pair (lo, hi, src->static_label); + } else { + emit_load_local64_to_pair (src->offset, lo, hi); + } + + } else { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (lo, src->static_label, src->size); + } else { + emit_load_local_to_reg (lo, src->offset, src->size); + } + + emit_extend_pair_high_from_low (lo, hi, src->size, src->is_unsigned); + + } + + if (postfix_incdec) { + + emit_push_reg_now (lo); + emit_push_reg_now (hi); + + emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret); + + emit_pop_reg_now (hi); + emit_pop_reg_now (lo); + + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION || + get_global_symbol_array (name) || + (!get_global_symbol_pointer_depth (name) && + get_global_symbol_size (name) > (DATA_PTR & 0x1f))) { + + /* + * In a 64-bit/pair context, an array/function/aggregate + * expression still decays to its address. The 32-bit RHS + * path already handled this, but this path was used when + * the caller requested eax:edx (for example a mis-sized + * pointer return). Loading the first word of the global + * table produced bogus pseudo-op table pointers such as the + * first string pointer instead of &table. + */ + emit_load_address_to_reg_now (lo, name); + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + } + + } + + } else if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) { + emit_load_global64_to_pair (lo, hi, name); + } else { + + emit_load_global_to_reg (lo, name, get_global_symbol_size (name)); + emit_extend_pair_high_from_low (lo, hi, get_global_symbol_size (name), get_global_symbol_unsigned (name)); + + } + + if (postfix_incdec) { + + emit_push_reg_now (lo); + emit_push_reg_now (hi); + + emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret); + + emit_pop_reg_now (hi); + emit_pop_reg_now (lo); + + } + + free (name); + return; + + } + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + free (name); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " xor %s, %s\n", lo, lo); + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } else { + + fprintf (state->ofp, " xorl %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + + } + + } + + return; + + } + + if (recover_unknown_rhs_identifier ()) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " xor %s, %s\n", lo, lo); + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } else { + + fprintf (state->ofp, " xorl %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + + } + + } + + return; + + } + + { + + int64_s v = const64_from_current_operand (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK); + fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK); + + } else { + + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo); + fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi); + + } + + } + + } + +} + +static void emit_assignment_divmod64_intel (int is_unsigned, int want_mod) { + + int loop_label, sub_label, nosub_label; + int lhs_pos_label, rhs_pos_label; + int qpos_label, rpos_label; + + loop_label = anon_label++; + sub_label = anon_label++; + nosub_label = anon_label++; + lhs_pos_label = anon_label++; + rhs_pos_label = anon_label++; + qpos_label = anon_label++; + rpos_label = anon_label++; + + if (state->syntax & ASM_SYNTAX_MASM) { + + fprintf (state->ofp, " sub esp, 40\n"); + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 4], edx\n"); + fprintf (state->ofp, " mov dword ptr [esp + 8], ebx\n"); + fprintf (state->ofp, " mov dword ptr [esp + 12], ecx\n"); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 16], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 20], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 24], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 28], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 32], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 36], eax\n"); + + if (!is_unsigned) { + + fprintf (state->ofp, " test dword ptr [esp + 4], 80000000h\n"); + fprintf (state->ofp, " jz L%d\n", lhs_pos_label); + fprintf (state->ofp, " not dword ptr [esp]\n"); + fprintf (state->ofp, " not dword ptr [esp + 4]\n"); + fprintf (state->ofp, " add dword ptr [esp], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 4], 0\n"); + fprintf (state->ofp, " mov dword ptr [esp + 32], 1\n"); + fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n"); + fprintf (state->ofp, "L%d:\n", lhs_pos_label); + fprintf (state->ofp, " test dword ptr [esp + 12], 80000000h\n"); + fprintf (state->ofp, " jz L%d\n", rhs_pos_label); + fprintf (state->ofp, " not dword ptr [esp + 8]\n"); + fprintf (state->ofp, " not dword ptr [esp + 12]\n"); + fprintf (state->ofp, " add dword ptr [esp + 8], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 12], 0\n"); + fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n"); + fprintf (state->ofp, "L%d:\n", rhs_pos_label); + + } + + fprintf (state->ofp, " mov esi, 64\n"); + fprintf (state->ofp, "L%d:\n", loop_label); + fprintf (state->ofp, " shl dword ptr [esp], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 4], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 24], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 28], 1\n"); + fprintf (state->ofp, " shl dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 20], 1\n"); + fprintf (state->ofp, " mov eax, dword ptr [esp + 28]\n"); + fprintf (state->ofp, " cmp eax, dword ptr [esp + 12]\n"); + fprintf (state->ofp, " ja L%d\n", sub_label); + fprintf (state->ofp, " jb L%d\n", nosub_label); + fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n"); + fprintf (state->ofp, " cmp eax, dword ptr [esp + 8]\n"); + fprintf (state->ofp, " jb L%d\n", nosub_label); + fprintf (state->ofp, "L%d:\n", sub_label); + fprintf (state->ofp, " mov eax, dword ptr [esp + 8]\n"); + fprintf (state->ofp, " sub dword ptr [esp + 24], eax\n"); + fprintf (state->ofp, " mov eax, dword ptr [esp + 12]\n"); + fprintf (state->ofp, " sbb dword ptr [esp + 28], eax\n"); + fprintf (state->ofp, " or dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, "L%d:\n", nosub_label); + fprintf (state->ofp, " dec esi\n"); + fprintf (state->ofp, " jnz L%d\n", loop_label); + + if (!is_unsigned) { + + fprintf (state->ofp, " cmp dword ptr [esp + 36], 0\n"); + fprintf (state->ofp, " je L%d\n", qpos_label); + fprintf (state->ofp, " not dword ptr [esp + 16]\n"); + fprintf (state->ofp, " not dword ptr [esp + 20]\n"); + fprintf (state->ofp, " add dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 20], 0\n"); + fprintf (state->ofp, "L%d:\n", qpos_label); + fprintf (state->ofp, " cmp dword ptr [esp + 32], 0\n"); + fprintf (state->ofp, " je L%d\n", rpos_label); + fprintf (state->ofp, " not dword ptr [esp + 24]\n"); + fprintf (state->ofp, " not dword ptr [esp + 28]\n"); + fprintf (state->ofp, " add dword ptr [esp + 24], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 28], 0\n"); + fprintf (state->ofp, "L%d:\n", rpos_label); + + } + + if (want_mod) { + + fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n"); + fprintf (state->ofp, " mov edx, dword ptr [esp + 28]\n"); + + } else { + + fprintf (state->ofp, " mov eax, dword ptr [esp + 16]\n"); + fprintf (state->ofp, " mov edx, dword ptr [esp + 20]\n"); + + } + + } else if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " sub esp, 40\n"); + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " mov dword [esp + 4], edx\n"); + fprintf (state->ofp, " mov dword [esp + 8], ebx\n"); + fprintf (state->ofp, " mov dword [esp + 12], ecx\n"); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " mov dword [esp + 16], eax\n"); + fprintf (state->ofp, " mov dword [esp + 20], eax\n"); + fprintf (state->ofp, " mov dword [esp + 24], eax\n"); + fprintf (state->ofp, " mov dword [esp + 28], eax\n"); + fprintf (state->ofp, " mov dword [esp + 32], eax\n"); + fprintf (state->ofp, " mov dword [esp + 36], eax\n"); + + if (!is_unsigned) { + + fprintf (state->ofp, " test dword [esp + 4], 80000000h\n"); + fprintf (state->ofp, " jz L%d\n", lhs_pos_label); + fprintf (state->ofp, " not dword [esp]\n"); + fprintf (state->ofp, " not dword [esp + 4]\n"); + fprintf (state->ofp, " add dword [esp], 1\n"); + fprintf (state->ofp, " adc dword [esp + 4], 0\n"); + fprintf (state->ofp, " mov dword [esp + 32], 1\n"); + fprintf (state->ofp, " xor dword [esp + 36], 1\n"); + fprintf (state->ofp, "L%d:\n", lhs_pos_label); + fprintf (state->ofp, " test dword [esp + 12], 80000000h\n"); + fprintf (state->ofp, " jz L%d\n", rhs_pos_label); + fprintf (state->ofp, " not dword [esp + 8]\n"); + fprintf (state->ofp, " not dword [esp + 12]\n"); + fprintf (state->ofp, " add dword [esp + 8], 1\n"); + fprintf (state->ofp, " adc dword [esp + 12], 0\n"); + fprintf (state->ofp, " xor dword [esp + 36], 1\n"); + fprintf (state->ofp, "L%d:\n", rhs_pos_label); + + } + + fprintf (state->ofp, " mov esi, 64\n"); + fprintf (state->ofp, "L%d:\n", loop_label); + fprintf (state->ofp, " shl dword [esp], 1\n"); + fprintf (state->ofp, " rcl dword [esp + 4], 1\n"); + fprintf (state->ofp, " rcl dword [esp + 24], 1\n"); + fprintf (state->ofp, " rcl dword [esp + 28], 1\n"); + fprintf (state->ofp, " shl dword [esp + 16], 1\n"); + fprintf (state->ofp, " rcl dword [esp + 20], 1\n"); + fprintf (state->ofp, " mov eax, dword [esp + 28]\n"); + fprintf (state->ofp, " cmp eax, dword [esp + 12]\n"); + fprintf (state->ofp, " ja L%d\n", sub_label); + fprintf (state->ofp, " jb L%d\n", nosub_label); + fprintf (state->ofp, " mov eax, dword [esp + 24]\n"); + fprintf (state->ofp, " cmp eax, dword [esp + 8]\n"); + fprintf (state->ofp, " jb L%d\n", nosub_label); + fprintf (state->ofp, "L%d:\n", sub_label); + fprintf (state->ofp, " mov eax, dword [esp + 8]\n"); + fprintf (state->ofp, " sub dword [esp + 24], eax\n"); + fprintf (state->ofp, " mov eax, dword [esp + 12]\n"); + fprintf (state->ofp, " sbb dword [esp + 28], eax\n"); + fprintf (state->ofp, " or dword [esp + 16], 1\n"); + fprintf (state->ofp, "L%d:\n", nosub_label); + fprintf (state->ofp, " dec esi\n"); + fprintf (state->ofp, " jnz L%d\n", loop_label); + + if (!is_unsigned) { + + fprintf (state->ofp, " cmp dword [esp + 36], 0\n"); + fprintf (state->ofp, " je L%d\n", qpos_label); + fprintf (state->ofp, " not dword [esp + 16]\n"); + fprintf (state->ofp, " not dword [esp + 20]\n"); + fprintf (state->ofp, " add dword [esp + 16], 1\n"); + fprintf (state->ofp, " adc dword [esp + 20], 0\n"); + fprintf (state->ofp, "L%d:\n", qpos_label); + fprintf (state->ofp, " cmp dword [esp + 32], 0\n"); + fprintf (state->ofp, " je L%d\n", rpos_label); + fprintf (state->ofp, " not dword [esp + 24]\n"); + fprintf (state->ofp, " not dword [esp + 28]\n"); + fprintf (state->ofp, " add dword [esp + 24], 1\n"); + fprintf (state->ofp, " adc dword [esp + 28], 0\n"); + fprintf (state->ofp, "L%d:\n", rpos_label); + + } + + if (want_mod) { + + fprintf (state->ofp, " mov eax, dword [esp + 24]\n"); + fprintf (state->ofp, " mov edx, dword [esp + 28]\n"); + + } else { + + fprintf (state->ofp, " mov eax, dword [esp + 16]\n"); + fprintf (state->ofp, " mov edx, dword [esp + 20]\n"); + + } + + } else { + + fprintf (state->ofp, " sub esp, 40\n"); + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 4], edx\n"); + fprintf (state->ofp, " mov dword ptr [esp + 8], ebx\n"); + fprintf (state->ofp, " mov dword ptr [esp + 12], ecx\n"); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 16], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 20], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 24], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 28], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 32], eax\n"); + fprintf (state->ofp, " mov dword ptr [esp + 36], eax\n"); + + if (!is_unsigned) { + + fprintf (state->ofp, " test dword ptr [esp + 4], 80000000h\n"); + fprintf (state->ofp, " jz .L%d\n", lhs_pos_label); + fprintf (state->ofp, " not dword ptr [esp]\n"); + fprintf (state->ofp, " not dword ptr [esp + 4]\n"); + fprintf (state->ofp, " add dword ptr [esp], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 4], 0\n"); + fprintf (state->ofp, " mov dword ptr [esp + 32], 1\n"); + fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n"); + fprintf (state->ofp, ".L%d:\n", lhs_pos_label); + fprintf (state->ofp, " test dword ptr [esp + 12], 80000000h\n"); + fprintf (state->ofp, " jz .L%d\n", rhs_pos_label); + fprintf (state->ofp, " not dword ptr [esp + 8]\n"); + fprintf (state->ofp, " not dword ptr [esp + 12]\n"); + fprintf (state->ofp, " add dword ptr [esp + 8], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 12], 0\n"); + fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n"); + fprintf (state->ofp, ".L%d:\n", rhs_pos_label); + + } + + fprintf (state->ofp, " mov esi, 64\n"); + fprintf (state->ofp, ".L%d:\n", loop_label); + fprintf (state->ofp, " shl dword ptr [esp], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 4], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 24], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 28], 1\n"); + fprintf (state->ofp, " shl dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, " rcl dword ptr [esp + 20], 1\n"); + fprintf (state->ofp, " mov eax, dword ptr [esp + 28]\n"); + fprintf (state->ofp, " cmp eax, dword ptr [esp + 12]\n"); + fprintf (state->ofp, " ja .L%d\n", sub_label); + fprintf (state->ofp, " jb .L%d\n", nosub_label); + fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n"); + fprintf (state->ofp, " cmp eax, dword ptr [esp + 8]\n"); + fprintf (state->ofp, " jb .L%d\n", nosub_label); + fprintf (state->ofp, ".L%d:\n", sub_label); + fprintf (state->ofp, " mov eax, dword ptr [esp + 8]\n"); + fprintf (state->ofp, " sub dword ptr [esp + 24], eax\n"); + fprintf (state->ofp, " mov eax, dword ptr [esp + 12]\n"); + fprintf (state->ofp, " sbb dword ptr [esp + 28], eax\n"); + fprintf (state->ofp, " or dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, ".L%d:\n", nosub_label); + fprintf (state->ofp, " dec esi\n"); + fprintf (state->ofp, " jnz .L%d\n", loop_label); + + if (!is_unsigned) { + + fprintf (state->ofp, " cmp dword ptr [esp + 36], 0\n"); + fprintf (state->ofp, " je .L%d\n", qpos_label); + fprintf (state->ofp, " not dword ptr [esp + 16]\n"); + fprintf (state->ofp, " not dword ptr [esp + 20]\n"); + fprintf (state->ofp, " add dword ptr [esp + 16], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 20], 0\n"); + fprintf (state->ofp, ".L%d:\n", qpos_label); + fprintf (state->ofp, " cmp dword ptr [esp + 32], 0\n"); + fprintf (state->ofp, " je .L%d\n", rpos_label); + fprintf (state->ofp, " not dword ptr [esp + 24]\n"); + fprintf (state->ofp, " not dword ptr [esp + 28]\n"); + fprintf (state->ofp, " add dword ptr [esp + 24], 1\n"); + fprintf (state->ofp, " adc dword ptr [esp + 28], 0\n"); + fprintf (state->ofp, ".L%d:\n", rpos_label); + + } + + if (want_mod) { + + fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n"); + fprintf (state->ofp, " mov edx, dword ptr [esp + 28]\n"); + + } else { + + fprintf (state->ofp, " mov eax, dword ptr [esp + 16]\n"); + fprintf (state->ofp, " mov edx, dword ptr [esp + 20]\n"); + + } + + } + + fprintf (state->ofp, " add esp, 40\n"); + +} + +static void emit_assignment_divmod64_att (int is_unsigned, int want_mod) { + + int loop_label, sub_label, nosub_label; + int lhs_pos_label, rhs_pos_label; + int qpos_label, rpos_label; + + loop_label = anon_label++; + sub_label = anon_label++; + nosub_label = anon_label++; + lhs_pos_label = anon_label++; + rhs_pos_label = anon_label++; + qpos_label = anon_label++; + rpos_label = anon_label++; + + fprintf (state->ofp, " subl $40, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " movl %%edx, 4(%%esp)\n"); + fprintf (state->ofp, " movl %%ebx, 8(%%esp)\n"); + fprintf (state->ofp, " movl %%ecx, 12(%%esp)\n"); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " movl %%eax, 16(%%esp)\n"); + fprintf (state->ofp, " movl %%eax, 20(%%esp)\n"); + fprintf (state->ofp, " movl %%eax, 24(%%esp)\n"); + fprintf (state->ofp, " movl %%eax, 28(%%esp)\n"); + fprintf (state->ofp, " movl %%eax, 32(%%esp)\n"); + fprintf (state->ofp, " movl %%eax, 36(%%esp)\n"); + + if (!is_unsigned) { + + fprintf (state->ofp, " testl $0x80000000, 4(%%esp)\n"); + fprintf (state->ofp, " jz .L%d\n", lhs_pos_label); + fprintf (state->ofp, " notl (%%esp)\n"); + fprintf (state->ofp, " notl 4(%%esp)\n"); + fprintf (state->ofp, " addl $1, (%%esp)\n"); + fprintf (state->ofp, " adcl $0, 4(%%esp)\n"); + fprintf (state->ofp, " movl $1, 32(%%esp)\n"); + fprintf (state->ofp, " xorl $1, 36(%%esp)\n"); + fprintf (state->ofp, ".L%d:\n", lhs_pos_label); + fprintf (state->ofp, " testl $0x80000000, 12(%%esp)\n"); + fprintf (state->ofp, " jz .L%d\n", rhs_pos_label); + fprintf (state->ofp, " notl 8(%%esp)\n"); + fprintf (state->ofp, " notl 12(%%esp)\n"); + fprintf (state->ofp, " addl $1, 8(%%esp)\n"); + fprintf (state->ofp, " adcl $0, 12(%%esp)\n"); + fprintf (state->ofp, " xorl $1, 36(%%esp)\n"); + fprintf (state->ofp, ".L%d:\n", rhs_pos_label); + + } + + fprintf (state->ofp, " movl $64, %%esi\n"); + fprintf (state->ofp, ".L%d:\n", loop_label); + fprintf (state->ofp, " shll $1, (%%esp)\n"); + fprintf (state->ofp, " rcll $1, 4(%%esp)\n"); + fprintf (state->ofp, " rcll $1, 24(%%esp)\n"); + fprintf (state->ofp, " rcll $1, 28(%%esp)\n"); + fprintf (state->ofp, " shll $1, 16(%%esp)\n"); + fprintf (state->ofp, " rcll $1, 20(%%esp)\n"); + fprintf (state->ofp, " movl 28(%%esp), %%eax\n"); + fprintf (state->ofp, " cmpl 12(%%esp), %%eax\n"); + fprintf (state->ofp, " ja .L%d\n", sub_label); + fprintf (state->ofp, " jb .L%d\n", nosub_label); + fprintf (state->ofp, " movl 24(%%esp), %%eax\n"); + fprintf (state->ofp, " cmpl 8(%%esp), %%eax\n"); + fprintf (state->ofp, " jb .L%d\n", nosub_label); + fprintf (state->ofp, ".L%d:\n", sub_label); + fprintf (state->ofp, " movl 8(%%esp), %%eax\n"); + fprintf (state->ofp, " subl %%eax, 24(%%esp)\n"); + fprintf (state->ofp, " movl 12(%%esp), %%eax\n"); + fprintf (state->ofp, " sbbl %%eax, 28(%%esp)\n"); + fprintf (state->ofp, " orl $1, 16(%%esp)\n"); + fprintf (state->ofp, ".L%d:\n", nosub_label); + fprintf (state->ofp, " decl %%esi\n"); + fprintf (state->ofp, " jnz .L%d\n", loop_label); + + if (!is_unsigned) { + + fprintf (state->ofp, " cmpl $0, 36(%%esp)\n"); + fprintf (state->ofp, " je .L%d\n", qpos_label); + fprintf (state->ofp, " notl 16(%%esp)\n"); + fprintf (state->ofp, " notl 20(%%esp)\n"); + fprintf (state->ofp, " addl $1, 16(%%esp)\n"); + fprintf (state->ofp, " adcl $0, 20(%%esp)\n"); + fprintf (state->ofp, ".L%d:\n", qpos_label); + fprintf (state->ofp, " cmpl $0, 32(%%esp)\n"); + fprintf (state->ofp, " je .L%d\n", rpos_label); + fprintf (state->ofp, " notl 24(%%esp)\n"); + fprintf (state->ofp, " notl 28(%%esp)\n"); + fprintf (state->ofp, " addl $1, 24(%%esp)\n"); + fprintf (state->ofp, " adcl $0, 28(%%esp)\n"); + fprintf (state->ofp, ".L%d:\n", rpos_label); + + } + + if (want_mod) { + + fprintf (state->ofp, " movl 24(%%esp), %%eax\n"); + fprintf (state->ofp, " movl 28(%%esp), %%edx\n"); + + } else { + + fprintf (state->ofp, " movl 16(%%esp), %%eax\n"); + fprintf (state->ofp, " movl 20(%%esp), %%edx\n"); + + } + + fprintf (state->ofp, " addl $40, %%esp\n"); + +} + +static void emit_assignment_divmod64 (int is_unsigned, int want_mod) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + emit_assignment_divmod64_intel (is_unsigned, want_mod); + } else { + emit_assignment_divmod64_att (is_unsigned, want_mod); + } + +} + +static void emit_assignment_binary_op64 (enum token_kind op, int is_unsigned) { + + int l1, l2; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " add eax, ebx\n"); + fprintf (state->ofp, " adc edx, ecx\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " sub eax, ebx\n"); + fprintf (state->ofp, " sbb edx, ecx\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + /** + * Low 64 bits of (EDX:EAX * EBX). The high half of the RHS + * is ignored for now; this is enough for constants/small ints. + */ + fprintf (state->ofp, " mov esi, eax\n"); + fprintf (state->ofp, " mov edi, edx\n"); + fprintf (state->ofp, " mul ebx\n"); + fprintf (state->ofp, " mov esi, edx\n"); + fprintf (state->ofp, " mov edx, edi\n"); + fprintf (state->ofp, " imul edx, ebx\n"); + fprintf (state->ofp, " add edx, esi\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + emit_assignment_divmod64 (is_unsigned, 0); + break; + + case TOK_MOD: case TOK_MODEQ: + + emit_assignment_divmod64 (is_unsigned, 1); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " and eax, ebx\n"); + fprintf (state->ofp, " and edx, ecx\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " or eax, ebx\n"); + fprintf (state->ofp, " or edx, ecx\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xor eax, ebx\n"); + fprintf (state->ofp, " xor edx, ecx\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " test ecx, ecx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), l2); + fprintf (state->ofp, " mov ecx, ebx\n"); + fprintf (state->ofp, " cmp ecx, 64\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l2); + fprintf (state->ofp, " cmp ecx, 32\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jb L%d\n" : " jb .L%d\n"), l1); + fprintf (state->ofp, " mov edx, eax\n"); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " sub ecx, 32\n"); + fprintf (state->ofp, " shl edx, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1); + fprintf (state->ofp, " shld edx, eax, cl\n"); + fprintf (state->ofp, " shl eax, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " xor edx, edx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1); + + anon_label++; + break; + + case TOK_RSH: case TOK_RSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " test ecx, ecx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), l2); + fprintf (state->ofp, " mov ecx, ebx\n"); + fprintf (state->ofp, " cmp ecx, 64\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l2); + fprintf (state->ofp, " cmp ecx, 32\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jb L%d\n" : " jb .L%d\n"), l1); + fprintf (state->ofp, " mov eax, edx\n"); + fprintf (state->ofp, is_unsigned ? " xor edx, edx\n" : " sar edx, 31\n"); + fprintf (state->ofp, " sub ecx, 32\n"); + fprintf (state->ofp, is_unsigned ? " shr eax, cl\n" : " sar eax, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1); + fprintf (state->ofp, " shrd eax, edx, cl\n"); + fprintf (state->ofp, is_unsigned ? " shr edx, cl\n" : " sar edx, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2); + fprintf (state->ofp, is_unsigned ? " xor eax, eax\n" : " mov eax, edx\n"); + fprintf (state->ofp, is_unsigned ? " xor edx, edx\n" : " sar edx, 31\n"); + fprintf (state->ofp, is_unsigned ? "" : " mov eax, edx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1); + + anon_label++; + break; + + default: + + break; + + } + + } else { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " addl %%ebx, %%eax\n"); + fprintf (state->ofp, " adcl %%ecx, %%edx\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " subl %%ebx, %%eax\n"); + fprintf (state->ofp, " sbbl %%ecx, %%edx\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " movl %%eax, %%esi\n"); + fprintf (state->ofp, " movl %%edx, %%edi\n"); + fprintf (state->ofp, " mull %%ebx\n"); + fprintf (state->ofp, " movl %%edx, %%esi\n"); + fprintf (state->ofp, " movl %%edi, %%edx\n"); + fprintf (state->ofp, " imull %%ebx, %%edx\n"); + fprintf (state->ofp, " addl %%esi, %%edx\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + emit_assignment_divmod64 (is_unsigned, 0); + break; + + case TOK_MOD: case TOK_MODEQ: + + emit_assignment_divmod64 (is_unsigned, 1); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " andl %%ebx, %%eax\n"); + fprintf (state->ofp, " andl %%ecx, %%edx\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " orl %%ebx, %%eax\n"); + fprintf (state->ofp, " orl %%ecx, %%edx\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xorl %%ebx, %%eax\n"); + fprintf (state->ofp, " xorl %%ecx, %%edx\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " testl %%ecx, %%ecx\n"); + fprintf (state->ofp, " jnz .L%d\n", l2); + fprintf (state->ofp, " movl %%ebx, %%ecx\n"); + fprintf (state->ofp, " cmpl $64, %%ecx\n"); + fprintf (state->ofp, " jae .L%d\n", l2); + fprintf (state->ofp, " cmpl $32, %%ecx\n"); + fprintf (state->ofp, " jb .L%d\n", l1); + fprintf (state->ofp, " movl %%eax, %%edx\n"); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " subl $32, %%ecx\n"); + fprintf (state->ofp, " shll %%cl, %%edx\n"); + fprintf (state->ofp, " jmp .L%d\n", l2 + 1); + fprintf (state->ofp, ".L%d:\n", l1); + fprintf (state->ofp, " shldl %%cl, %%eax, %%edx\n"); + fprintf (state->ofp, " shll %%cl, %%eax\n"); + fprintf (state->ofp, " jmp .L%d\n", l2 + 1); + fprintf (state->ofp, ".L%d:\n", l2); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " xorl %%edx, %%edx\n"); + fprintf (state->ofp, ".L%d:\n", l2 + 1); + + anon_label++; + break; + + case TOK_RSH: case TOK_RSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " testl %%ecx, %%ecx\n"); + fprintf (state->ofp, " jnz .L%d\n", l2); + fprintf (state->ofp, " movl %%ebx, %%ecx\n"); + fprintf (state->ofp, " cmpl $64, %%ecx\n"); + fprintf (state->ofp, " jae .L%d\n", l2); + fprintf (state->ofp, " cmpl $32, %%ecx\n"); + fprintf (state->ofp, " jb .L%d\n", l1); + fprintf (state->ofp, " movl %%edx, %%eax\n"); + fprintf (state->ofp, is_unsigned ? " xorl %%edx, %%edx\n" : " sarl $31, %%edx\n"); + fprintf (state->ofp, " subl $32, %%ecx\n"); + fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%eax\n" : " sarl %%cl, %%eax\n"); + fprintf (state->ofp, " jmp .L%d\n", l2 + 1); + fprintf (state->ofp, ".L%d:\n", l1); + fprintf (state->ofp, " shrdl %%cl, %%edx, %%eax\n"); + fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%edx\n" : " sarl %%cl, %%edx\n"); + fprintf (state->ofp, " jmp .L%d\n", l2 + 1); + fprintf (state->ofp, ".L%d:\n", l2); + fprintf (state->ofp, is_unsigned ? " xorl %%eax, %%eax\n" : " movl %%edx, %%eax\n"); + fprintf (state->ofp, is_unsigned ? " xorl %%edx, %%edx\n" : " sarl $31, %%edx\n"); + fprintf (state->ofp, is_unsigned ? "" : " movl %%edx, %%eax\n"); + fprintf (state->ofp, ".L%d:\n", l2 + 1); + + anon_label++; + break; + + default: + + break; + + } + + } + +} + +static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line); +static void emit_assignment_binary_op (enum token_kind op, int is_unsigned); +static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size); +static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size); +static void emit_load_deref_reg_now (const char *reg, int size); + +static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size); +static void emit_load_floating_rhs_expression_now (int result_size); + +static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned); +static int token_is_floating_constant_now (void); + +static void emit_load_local_address_to_reg_now (const char *reg, long offset) { + + char memref[64]; + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), offset); + fprintf (state->ofp, " lea %s, %s\n", reg, memref); + + } else { + fprintf (state->ofp, " leal %ld(%%ebp), %%%s\n", offset, reg); + } + +} + +static void emit_apply_postfix_member_access_to_reg_now (const char *reg) { + + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = 0; + postfix_member_seen = 0; + postfix_member_offset = 0; + postfix_member_size = 0; + postfix_member_is_floating = 0; + postfix_member_is_unsigned = 0; + + { + + const char *current_object_tag_name = postfix_copy_lvalue_tag_name; + int current_object_size = postfix_copy_lvalue_size; + + if (current_object_size <= 0 && rhs_last_pointed_size > 0) { + current_object_size = rhs_last_pointed_size; + } + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int size = DATA_INT & 0x1f; + int elem_size = DATA_INT & 0x1f; + int pointer_depth = 0; + int is_array = 0; + int is_floating = 0; + int is_unsigned = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", op == TOK_ARROW ? "->" : "."); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + return; + + } + + is_unsigned = last_found_member_is_unsigned; + + { + + const char *member_tag_name = last_found_member_tag_name; + + /* + * For an array member whose element type is a pointer, the + * subscript step is always one pointer. Keep this independent + * of the pointed aggregate size; otherwise expressions like + * ic->heads[i] scale by sizeof(*heads[i]) and are later passed + * as an aggregate instead of one pointer. + */ + if (is_array && pointer_depth > 0) { + elem_size = DATA_PTR; + } else if (pointer_depth > 1) { + elem_size = DATA_PTR; + } else if (pointer_depth == 1 && member_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0); + + if (entry) { + elem_size = entry->size; + } + + } + + current_object_tag_name = member_tag_name; + + } + + free (member); + + postfix_member_seen = 1; + postfix_member_pointer_depth = is_array ? 1 : pointer_depth; + postfix_member_pointed_size = elem_size; + postfix_member_offset = offset; + postfix_member_size = size; + postfix_member_is_floating = is_floating; + postfix_member_is_unsigned = is_unsigned; + + if (pointer_depth > 0 || is_array) { + current_object_size = elem_size; + } else { + current_object_size = size; + } + + if (tok.kind == TOK_LBRACK) { + + enum token_kind assign_op; + int subscript_elem_size; + + if (!is_array && pointer_depth > 0) { + emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f); + } else if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + subscript_elem_size = index_step_size (elem_size); + + if (subscript_elem_size <= 0) { + subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size); + + if (tok.kind == TOK_ARROW && subscript_elem_size <= (DATA_PTR & 0x1f)) { + + emit_load_deref_reg_now (reg, DATA_PTR & 0x1f); + + postfix_member_pointer_depth = 0; + postfix_member_size = DATA_PTR & 0x1f; + + continue; + + } + + if (is_assignment_operator (tok.kind)) { + + assign_op = tok.kind; + get_token (); + + if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) + && tok.kind == TOK_IDENT && tok.ident + && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + + unsigned long rhs_line = get_line_number (); + emit_push_reg_now (reg); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + postfix_member_seen = 0; + return; + + } + + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN) { + + if (subscript_elem_size > (DATA_LLONG & 0x1f)) { + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + emit_load_assignment_rhs_expression_to_reg (reg); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + + postfix_member_seen = 0; + return; + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + + } else { + + emit_load_deref_reg_now (reg, subscript_elem_size); + emit_push_reg_now (reg); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size); + + postfix_member_seen = 0; + return; + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + current_object_size = elem_size; + continue; + + } + + /** + * The expression now denotes the subscripted element, not the + * whole member array. Keep the postfix-member metadata in + * step with the value in REG so argument passing does not + * mistake e.g. ic->heads[i] for the entire heads[] aggregate + * and push multiple words. + * + * If the element itself is an aggregate, REG must remain the + * address of that element. Loading *(REG) would fetch the + * first word of the struct and later aggregate argument + * passing would treat that word as a pointer, as happened for + * instruction.types[instruction.operands]. + */ + postfix_member_size = subscript_elem_size; + + /* + * Subscript of an array member whose element type is a pointer + * yields the pointer stored in that array slot. The generic + * aggregate-subscript path deliberately keeps struct elements as + * addresses, but pointer elements must still be loaded before a + * following -> member access. Otherwise expressions such as + * instruction.regs[operand]->type.reg_rex treat the address of + * the regs[] slot as if it were a struct reg_entry *. + */ + if (is_array && (pointer_depth > 0 || (tok.kind == TOK_ARROW && subscript_elem_size == (DATA_PTR & 0x1f)))) { + + emit_load_deref_reg_now (reg, DATA_PTR & 0x1f); + + postfix_member_pointer_depth = pointer_depth > 0 ? pointer_depth - 1 : 0; + postfix_member_size = DATA_PTR & 0x1f; + + if (postfix_member_pointer_depth == 0 && current_object_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (current_object_tag_name, 0); + + if (entry) { + current_object_size = entry->size; + } + + } + + continue; + + } + + if (postfix_member_pointer_depth > 0) { + postfix_member_pointer_depth--; + } + + if (postfix_member_pointer_depth != 0 + || postfix_member_size <= (DATA_PTR & 0x1f)) { + emit_load_deref_reg_now (reg, subscript_elem_size); + } + + continue; + + } + + if (is_array && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + /* + * A bare array member expression decays to a pointer when used + * as an rvalue/function argument. Keep REG as the element + * address, but do not leave aggregate element metadata behind or + * the call argument path will push the first element by value. + * This broke calls such as _cpp_add_unknown2_direct(..., + * macro->tokens, token_count), where tokens[0] is a struct. + */ + postfix_member_size = DATA_PTR & 0x1f; + postfix_member_pointer_depth = 1; + postfix_member_pointed_size = elem_size; + + continue; + + } + + if ((tok.kind == TOK_ARROW || tok.kind == TOK_DOT) && pointer_depth == 0) { + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + continue; + + } + + if (size > (DATA_PTR & 0x1f) && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + continue; + + } + + if (state->ofp) { + + if (strcmp (reg, "edx") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%edx\n", reg); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *opsize = "dword"; + + if (size == 1) { + opsize = "byte"; + } else if (size == 2) { + opsize = "word"; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == 1) { + fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", reg, reg, offset); + } else if (size == 2) { + fprintf (state->ofp, " movzx %s, word [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " mov %s, %s [%s + %d]\n", reg, opsize, reg, offset); + } + + } else { + + if (size == 1) { + fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", reg, reg, offset); + } else if (size == 2) { + fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " mov %s, %s ptr [%s + %d]\n", reg, opsize, reg, offset); + } + + } + + } else { + + if (size == 1) { + fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, reg, reg); + } else if (size == 2) { + fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, reg); + } else { + fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg); + } + + } + + } + + } + + } + +} + +static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op) { + + if (!postfix_member_seen) { + return; + } + + if (state->ofp) { + + const char *insn = op == TOK_INCR ? "inc" : "dec"; + (void) reg; + + if (postfix_member_pointer_depth > 0) { + + int step = postfix_member_pointed_size; + const char *arith = op == TOK_INCR ? "add" : "sub"; + + if (step <= 0) { + step = 1; + } + + if (step == 1) { + /* Falling through keeps the compact inc/dec form for char *. */ + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s dword [edx + %d], %d\n", arith, postfix_member_offset, step); + } else { + fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", arith, postfix_member_offset, step); + } + + return; + + } else { + + fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", arith, step, postfix_member_offset); + return; + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *opsize = "dword"; + + if (postfix_member_size == 1) { + opsize = "byte"; + } else if (postfix_member_size == 2) { + opsize = "word"; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s [edx + %d]\n", insn, opsize, postfix_member_offset); + } else { + fprintf (state->ofp, " %s %s ptr [edx + %d]\n", insn, opsize, postfix_member_offset); + } + + } else { + + if (postfix_member_size == 1) { + fprintf (state->ofp, " %sb %d(%%edx)\n", insn, postfix_member_offset); + } else if (postfix_member_size == 2) { + fprintf (state->ofp, " %sw %d(%%edx)\n", insn, postfix_member_offset); + } else { + fprintf (state->ofp, " %sl %d(%%edx)\n", insn, postfix_member_offset); + } + + } + + } + +} + +static int rhs_text_is_plain_aggregate_lvalue_now (const char *p) { + + int paren_depth = 0; + int bracket_depth = 0; + int saw_ident = 0; + + if (!p) { + return 1; + } + + while (*p) { + + unsigned char ch = (unsigned char) *p; + + if (ch == '\'' || ch == '"') { + + int quote = ch; + p++; + + while (*p && *p != quote) { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p == quote) { + p++; + } + + continue; + + } + + if (ch == '(') { + + paren_depth++; + p++; + + continue; + + } + + if (ch == ')') { + + if (paren_depth == 0 && bracket_depth == 0) { + break; + } + + if (paren_depth > 0) { + paren_depth--; + } + + p++; + continue; + + } + + if (ch == '[') { + + bracket_depth++; + p++; + + continue; + + } + + if (ch == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + p++; + continue; + + } + + if (paren_depth == 0 && bracket_depth == 0) { + + if (ch == ';' || ch == ',') { + break; + } + + if (ch == '-' && p[1] == '>') { + + p += 2; + continue; + + } + + if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || + ch == '&' || ch == '|' || ch == '^' || ch == '?' || ch == ':') { + return 0; + } + + } + + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_') { + saw_ident = 1; + } + + p++; + + } + + return saw_ident; + +} + +static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size); +static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line); + +static int emit_store_assignment_to_aggregate_address_now (const char *addr_reg, int size, const char *name_start, const char *name_caret, unsigned long name_line) { + + enum token_kind op; + + if (!is_assignment_operator (tok.kind) || size <= (DATA_LLONG & 0x1f)) { + return 0; + } + + op = tok.kind; + get_token (); + + if (op == TOK_ASSIGN && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start ? tok.start : name_start; + const char *rhs_caret = tok.caret ? tok.caret : name_caret; + + unsigned long rhs_line = get_line_number (); + emit_push_reg_now (addr_reg); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + return 1; + + } + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "aggregate assignment expression not implemented"); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + return 1; + +} + +static int emit_store_assignment_to_postfix_member_now (const char *reg) { + + enum token_kind op; + int assign_member_offset; + int assign_member_size; + + if (!postfix_member_seen || !is_assignment_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + assign_member_offset = postfix_member_offset; + assign_member_size = postfix_member_size; + + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + if (postfix_member_is_floating || token_is_floating_constant_now ()) { + + emit_push_reg_now ("edx"); + emit_load_floating_rhs_expression_now (assign_member_size); + + emit_pop_reg_now ("edx"); + emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size); + + return 1; + + } + + if (assign_member_size > (DATA_LLONG & 0x1f) && + tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + + unsigned long rhs_line = get_line_number (); + emit_push_reg_now ("edx"); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = assign_member_offset; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + return 1; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) { + return 1; + } + + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + + } else { + + emit_push_reg_now ("edx"); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size); + + } else { + emit_load_assignment_rhs_expression_to_reg (reg); + } + + return 1; + +} + +static int parenthesized_function_designator_call_now (void) { + + const char *p; + + if (tok.kind != TOK_IDENT || !tok.caret || !tok.ident) { + return 0; + } + + p = tok.caret + strlen (tok.ident); + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t') { + p++; + } + + return *p == '('; + +} + +static void emit_load_deref_reg_now (const char *reg, int size) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte [%s]\n", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word [%s]\n", reg, reg); + } else { + fprintf (state->ofp, " mov %s, dword [%s]\n", reg, reg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte ptr [%s]\n", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word ptr [%s]\n", reg, reg); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s]\n", reg, reg); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl (%%%s), %%%s\n", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl (%%%s), %%%s\n", reg, reg); + } else { + fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, reg); + } + + } + +} + +static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size) { + + if (suppress_next_struct_return_scalar_store) { + + suppress_next_struct_return_scalar_store = 0; + return; + + } + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " mov byte [%s], %cl\n", addr_reg, value_reg[1]); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " mov word [%s], %cx\n", addr_reg, value_reg[1]); + } else { + fprintf (state->ofp, " mov dword [%s], %s\n", addr_reg, value_reg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " mov byte ptr [%s], %cl\n", addr_reg, value_reg[1]); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " mov word ptr [%s], %cx\n", addr_reg, value_reg[1]); + } else { + fprintf (state->ofp, " mov dword ptr [%s], %s\n", addr_reg, value_reg); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%cl, (%%%s)\n", value_reg[1], addr_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%cx, (%%%s)\n", value_reg[1], addr_reg); + } else { + fprintf (state->ofp, " movl %%%s, (%%%s)\n", value_reg, addr_reg); + } + + } + +} + +static int emit_handle_subscript_after_loaded_pointer_to_reg_now (const char *reg, int pointer_depth, int pointed_size) { + + int subscript_elem_size; + + if (tok.kind != TOK_LBRACK) { + return 0; + } + + subscript_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : pointed_size; + + if (subscript_elem_size <= 0) { + subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + get_token (); + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + emit_load_deref_reg_now (reg, subscript_elem_size); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + + emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size); + clear_rhs_last_pointer_info (); + + return 1; + + } + + emit_load_deref_reg_now (reg, subscript_elem_size); + + if (pointer_depth > 1) { + set_rhs_last_pointer_info (pointer_depth - 1, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + return 1; + +} + +static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", dst_reg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word [%s + %d]\n", dst_reg, addr_reg, offset); + } else { + fprintf (state->ofp, " mov %s, dword [%s + %d]\n", dst_reg, addr_reg, offset); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", dst_reg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", dst_reg, addr_reg, offset); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", dst_reg, addr_reg, offset); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg); + } else { + fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg); + } + + } + +} + +static int emit_load_address_of_parenthesized_postfix_to_reg_now (const char *reg) { + + char *name; + + enum token_kind member_op; + struct local_symbol *src; + + int global_index; + + const char *current_object_tag_name = 0; + int current_object_size = 0; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + int64_s offset_value; + + if (parse_constexpr_null_member_address_after_lparen (&offset_value)) { + + emit_load_const32_to_reg_now (reg, offset_value); + return 1; + + } + + } + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&('"); + + expect (TOK_RPAREN, ")"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + if (src->is_array) { + + current_object_size = src->pointed_size > 0 ? src->pointed_size : src->size; + current_object_tag_name = src->pointed_tag_name; + + if (src->is_static && src->static_label) { + emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1); + } + + } else if (src->pointer_depth > 0) { + + current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR; + current_object_tag_name = src->pointed_tag_name; + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + } else if (src->is_static && src->static_label) { + + current_object_size = src->size; + emit_load_address_to_reg_now (reg, src->static_label); + + } else { + + current_object_size = src->size; + + /** + * A typedef such as section_t can hide the pointer depth from this + * older parser. For &(p->member), the base must be the pointer + * value stored in the local, not the address of the local slot. + * + * Keep the pointed aggregate tag as well. Without that, address-of + * member expressions such as &symbol->next can fall back to an + * unqualified member-name lookup and pick another struct's "next" + * field. That emitted symbol + 24 instead of symbol + 36 for + * struct symbol::next, corrupting symbol->section in pdas. + */ + if (tok.kind == TOK_ARROW && src->size == (DATA_PTR & 0x1f)) { + + if (src->pointed_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (src->pointed_tag_name, 0); + current_object_tag_name = src->pointed_tag_name; + + if (entry) { + current_object_size = entry->size; + } + + } + + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + } + + } else if (global_index >= 0) { + + if (get_global_symbol_array (name)) { + + current_object_size = get_global_symbol_pointed_size (name); + emit_load_symbol_address_to_reg_now (reg, name, 0, 0); + + } else if (get_global_symbol_pointer_depth (name) > 0 || (tok.kind == TOK_ARROW && get_global_symbol_size (name) == (DATA_PTR & 0x1f))) { + + current_object_size = get_global_symbol_pointed_size (name); + + if (current_object_size <= 0) { + current_object_size = DATA_PTR & 0x1f; + } + + emit_load_global_to_reg (reg, name, DATA_PTR); + + } else { + + current_object_size = get_global_symbol_size (name); + emit_load_address_to_reg_now (reg, name); + + } + + } else { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", name); + free (name); + + expect (TOK_RPAREN, ")"); + return 1; + + } + + if (tok.kind == TOK_LBRACK) { + + int elem_size = DATA_INT & 0x1f; + + if (src) { + + elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) : + (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size); + + } else if (global_index >= 0) { + + elem_size = get_global_symbol_array (name) ? + (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) : + (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name)); + + } + + if (elem_size <= 0) { + elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + } + + free (name); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset; + int size; + int elem_size; + int pointer_depth; + + member_op = tok.kind; + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + expect (TOK_RPAREN, ")"); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + offset = 0; + size = DATA_INT & 0x1f; + elem_size = DATA_INT & 0x1f; + pointer_depth = 0; + + if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + expect (TOK_RPAREN, ")"); + + return 1; + + } + + current_object_tag_name = last_found_member_tag_name; + free (member); + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth > 0) { + + emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f); + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + } else { + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + } + + } else { + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + } + + if (pointer_depth > 0) { + current_object_size = elem_size > 0 ? elem_size : DATA_PTR; + } else { + current_object_size = size; + } + + } + + expect (TOK_RPAREN, ")"); + return 1; + +} + +static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size) { + + if (!state->ofp) { + return; + } + + if (offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", addr_reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, addr_reg); + } + + } + + emit_store_reg_to_deref_reg_now (addr_reg, value_reg, size); + +} + +static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size) { + + if (!state->ofp) { + return; + } + + if (offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", addr_reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, addr_reg); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fstp %s [%s]\n" : " fstp %s ptr [%s]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", addr_reg); + } else { + fprintf (state->ofp, " fstp%s (%%%s)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", addr_reg); + } + +} + +static void emit_load_symbol_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_address_to_reg_now (reg, sym->static_label); + } else { + emit_load_local_address_to_reg_now (reg, sym->offset); + } + + } else { + emit_load_address_to_reg_now (reg, name); + } + +} + +static void emit_copy_fixed_size_now (int size) { + + int offset = 0; + + if (!state->ofp) { + return; + } + + while (size - offset >= 4) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ecx, dword [eax + %d]\n" : " mov ecx, dword ptr [eax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [edx + %d], ecx\n" : " mov dword ptr [edx + %d], ecx\n", offset); + + } else { + + fprintf (state->ofp, " movl %d(%%eax), %%ecx\n", offset); + fprintf (state->ofp, " movl %%ecx, %d(%%edx)\n", offset); + + } + + offset += 4; + + } + + while (size - offset >= 2) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov cx, word [eax + %d]\n" : " mov cx, word ptr [eax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [edx + %d], cx\n" : " mov word ptr [edx + %d], cx\n", offset); + + } else { + + fprintf (state->ofp, " movw %d(%%eax), %%cx\n", offset); + fprintf (state->ofp, " movw %%cx, %d(%%edx)\n", offset); + + } + + offset += 2; + + } + + while (size - offset >= 1) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov cl, byte [eax + %d]\n" : " mov cl, byte ptr [eax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov byte [edx + %d], cl\n" : " mov byte ptr [edx + %d], cl\n", offset); + + } else { + + fprintf (state->ofp, " movb %d(%%eax), %%cl\n", offset); + fprintf (state->ofp, " movb %%cl, %d(%%edx)\n", offset); + + } + + offset++; + + } + +} + +static void emit_memcpy_symbol_to_symbol_now (struct local_symbol *dst, const char *dst_name, struct local_symbol *src, const char *src_name, int size) { + + emit_load_symbol_address_for_copy_now ("eax", src, src_name); + emit_load_symbol_address_for_copy_now ("edx", dst, dst_name); + emit_copy_fixed_size_now (size); + +} + +static int token_identifier_is_function_call_rhs_now (void) { + + struct token *saved_tok; + int is_call; + + if (tok.kind != TOK_IDENT || !tok.ident) { + return 0; + } + + /** + * Do not inspect tok.start/tok.caret to decide whether this identifier is + * followed by a call. After macro substitution the spelling in the source + * can be a different length from tok.ident, e.g. + * + * #define cc_parse_type ccpartype + * member.type = cc_parse_type(reader); + * + * tok.ident is "ccpartype" but tok.caret still points into the original + * text "cc_parse_type(reader)". The old spelling-based test missed the + * '(' and the aggregate-copy fast path treated the function symbol as an + * object, leaving the argument list unconsumed and producing "expected ;". + */ + saved_tok = xmalloc (sizeof (*saved_tok)); + *saved_tok = tok; + + if (tok.ident) { + saved_tok->ident = xstrdup (tok.ident); + } + + if (tok.start) { + + const char *old_start = tok.start; + const char *old_caret = tok.caret; + + saved_tok->start = xstrdup (old_start); + + if (old_caret && old_caret >= old_start) { + saved_tok->caret = saved_tok->start + (old_caret - old_start); + } else { + saved_tok->caret = saved_tok->start; + } + + } + + get_token (); + + is_call = (tok.kind == TOK_LPAREN); + unget_token (saved_tok); + + return is_call; + +} + +static struct token *clone_current_token_now (void) { + + struct token *saved_tok = xmalloc (sizeof (*saved_tok)); + *saved_tok = tok; + + if (tok.ident) { + saved_tok->ident = xstrdup (tok.ident); + } + + if (tok.start) { + + const char *old_start = tok.start; + const char *old_caret = tok.caret; + + saved_tok->start = xstrdup (old_start); + + if (old_caret && old_caret >= old_start) { + saved_tok->caret = saved_tok->start + (old_caret - old_start); + } else { + saved_tok->caret = saved_tok->start; + } + + } + + return saved_tok; + +} + +static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size) { + + struct local_symbol *rhs_sym; + char *rhs_name; + + const char *rhs_start; + const char *rhs_caret; + + unsigned long rhs_line; + + int rhs_global_index; + int rhs_size; + + int source_size; + int source_ready; + + /* + * Only use the aggregate-copy fast path when the destination is + * itself an aggregate object. Pointer members can legally be assigned + * an array object, e.g. fp->intBuffer = buffer1; in pdpclib. The RHS + * symbol then has a large array size, but the LHS is only a pointer, so + * emitting a fixed-size aggregate copy corrupts the FILE object. + */ + if (size < (DATA_LLONG & 0x1f)) { + return 0; + } + + if (tok.kind == TOK_STAR) { + + struct token *saved_tok = clone_current_token_now (); + struct local_symbol *ptr_sym; + + char *ptr_name; + + int ptr_global_index; + int ptr_depth; + int ptr_pointed_size; + + get_token (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + unget_token (saved_tok); + return 0; + + } + + ptr_name = xstrdup (tok.ident); + ptr_sym = find_local_symbol (ptr_name); + ptr_global_index = find_global_symbol (ptr_name); + + if (!ptr_sym && ptr_global_index < 0) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name); + ptr_pointed_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name); + + if (!ptr_sym && ptr_global_index >= 0 + && get_global_symbol_kind (ptr_name) == GLOBAL_SYMBOL_FUNCTION) { + + const char *call_start = tok.start; + const char *call_caret = tok.caret; + unsigned long call_line = get_line_number (); + + get_token (); + + if (tok.kind != TOK_LPAREN || ptr_depth <= 0 + || (ptr_depth == 1 && ptr_pointed_size > 0 && ptr_pointed_size < size)) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + emit_push_reg_now (addr_reg); + emit_call_identifier_to_reg_now (ptr_name, "eax", call_start, call_caret, call_line); + emit_pop_reg_now ("edx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (ptr_name); + return 1; + + } + + /* + * For aggregate initialization from a dereferenced pointer, keep the + * full pointed-to object size. The old code masked pointed_size with + * 0x1f as if it were a DATA_* scalar type. That turns e.g. + * + * struct hashtab old_hashtab = *hashtab; + * + * from a 44-byte copy into a rejected aggregate fast path because + * 44 & 0x1f == 12. The initializer then falls back to scalar code + * and copies only the first word, leaving old_hashtab mostly + * uninitialized and crashing in rehash(). + */ + source_size = ptr_depth > 1 ? (DATA_PTR & 0x1f) : ptr_pointed_size; + + if (ptr_depth <= 0 || source_size < size) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + emit_push_reg_now (addr_reg); + + if (ptr_sym) { + + if (ptr_sym->is_static && ptr_sym->static_label) { + emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("eax", ptr_name, DATA_PTR); + } + + get_token (); + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int member_is_array = 0; + int member_incdec = 0; + + enum token_kind member_incdec_op = TOK_EOF; + const char *object_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0; + + int object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name); + get_token (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, object_size, object_tag_name, + &member_offset, &member_size, &member_elem_size, + &member_pointer_depth, &member_is_array, 0)) { + + free (member); + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + member_incdec = 1; + member_incdec_op = tok.kind; + + get_token (); + + } + + if (member_op != TOK_ARROW || member_pointer_depth <= 0 || member_elem_size < size) { + + free (member); + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + emit_push_reg_now (addr_reg); + + if (ptr_sym) { + + if (ptr_sym->is_static && ptr_sym->static_label) { + emit_load_global_to_reg ("ecx", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", ptr_name, DATA_PTR); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? + " mov eax, dword [ecx + %d]\n" : + " mov eax, dword ptr [ecx + %d]\n", member_offset); + + if (member_incdec) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? + " %s dword [ecx + %d], %d\n" : + " %s dword ptr [ecx + %d], %d\n", + member_incdec_op == TOK_INCR ? "add" : "sub", + member_offset, member_elem_size); + + } + + } else { + + fprintf (state->ofp, " movl %d(%%ecx), %%eax\n", member_offset); + + if (member_incdec) { + + fprintf (state->ofp, " %sl $%d, %d(%%ecx)\n", + member_incdec_op == TOK_INCR ? "add" : "sub", + member_elem_size, member_offset); + + } + + } + + } + + emit_pop_reg_now ("edx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (member); + free (ptr_name); + + return 1; + + } + + emit_pop_reg_now ("edx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (ptr_name); + return 1; + + } + + if (tok.kind == TOK_LPAREN) { + + struct token *saved_tok = clone_current_token_now (); + struct local_symbol *ptr_sym; + + char *ptr_name; + + const char *ptr_start; + const char *ptr_caret; + + unsigned long ptr_line; + + int ptr_global_index; + int ptr_depth; + + const char *ptr_tag_name; + + int current_object_size; + int have_direct_object_pointer; + + get_token (); + + if (tok.kind != TOK_STAR) { + + unget_token (saved_tok); + return 0; + + } + + get_token (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + unget_token (saved_tok); + return 0; + + } + + ptr_name = xstrdup (tok.ident); + ptr_start = tok.start; + ptr_caret = tok.caret; + ptr_line = get_line_number (); + ptr_sym = find_local_symbol (ptr_name); + ptr_global_index = find_global_symbol (ptr_name); + + if (!ptr_sym && ptr_global_index < 0) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name); + ptr_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0; + + current_object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name); + source_size = current_object_size; + + if (ptr_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (ptr_tag_name, 0); + + if (entry) { + + current_object_size = entry->size; + source_size = current_object_size; + + } + + } + + get_token (); + + if (tok.kind != TOK_RPAREN || ptr_depth <= 0) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + get_token (); + + if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + free (ptr_name); + + unget_token (saved_tok); + return 0; + + } + + emit_push_reg_now (addr_reg); + + if (ptr_sym) { + + if (ptr_sym->is_static && ptr_sym->static_label) { + emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("eax", ptr_name, DATA_PTR); + } + + emit_load_deref_reg_now ("eax", DATA_PTR); + have_direct_object_pointer = 1; + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) { + + if (tok.kind == TOK_LBRACK) { + + emit_parse_postfix_subscript_scaled_address_to_reg_now ("eax", DATA_PTR); + + current_object_size = DATA_PTR; + continue; + + } + + { + + enum token_kind member_op = tok.kind; + char *member; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int member_is_array = 0; + + get_token (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, + "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (ptr_name); + return 0; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, ptr_tag_name, + &member_offset, &member_size, &member_elem_size, + &member_pointer_depth, &member_is_array, 0)) { + + report_line_at (get_filename (), ptr_line, REPORT_ERROR, ptr_start, ptr_caret, + "unknown member '%s'", member); + + free (member); + free (ptr_name); + + return 0; + + } + + ptr_tag_name = last_found_member_tag_name; + + if (member_op == TOK_ARROW) { + + if (have_direct_object_pointer) { + have_direct_object_pointer = 0; + } else { + emit_load_deref_reg_now ("eax", DATA_PTR); + } + + } else { + have_direct_object_pointer = 0; + } + + if (member_offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add eax, %d\n", member_offset); + } else { + fprintf (state->ofp, " addl $%d, %%eax\n", member_offset); + } + + } + + current_object_size = member_size; + source_size = member_size; + + free (member); + + } + + } + + if (source_size < size) { + + free (ptr_name); + return 0; + + } + + emit_pop_reg_now ("edx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (ptr_name); + return 1; + + } + + if (tok.kind != TOK_IDENT) { + return 0; + } + + /* + * A function-call RHS that returns a struct/union must be parsed by + * the normal expression code. The aggregate-copy fast path is only + * for copying an already-existing aggregate object by address. + */ + if (token_identifier_is_function_call_rhs_now ()) { + return 0; + } + + if (!rhs_text_is_plain_aggregate_lvalue_now (tok.caret ? tok.caret : tok.start)) { + return 0; + } + + rhs_name = xstrdup (tok.ident); + rhs_start = tok.start; + rhs_caret = tok.caret; + rhs_line = get_line_number (); + + rhs_sym = find_local_symbol (rhs_name); + rhs_global_index = find_global_symbol (rhs_name); + + if (!rhs_sym && rhs_global_index < 0) { + + free (rhs_name); + return 0; + + } + + rhs_size = rhs_sym ? rhs_sym->size : get_global_symbol_size (rhs_name); + + { + + struct token *saved_rhs_tok = clone_current_token_now (); + get_token (); + + if (rhs_size <= (DATA_LLONG & 0x1f) && tok.kind != TOK_DOT && tok.kind != TOK_ARROW && tok.kind != TOK_LBRACK) { + + free (rhs_name); + + unget_token (saved_rhs_tok); + return 0; + + } + + unget_token (saved_rhs_tok); + + } + + emit_push_reg_now (addr_reg); + get_token (); + + source_ready = 0; + source_size = rhs_size; + + if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) { + + if (!emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, rhs_start, rhs_caret, rhs_line)) { + + free (rhs_name); + return 0; + + } + + source_size = postfix_copy_lvalue_size; + source_ready = 1; + + } + + if (source_size < size) { + + free (rhs_name); + return 0; + + } + + if (!source_ready) { + emit_load_symbol_address_for_copy_now ("eax", rhs_sym, rhs_name); + } + + emit_pop_reg_now ("edx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (rhs_name); + return 1; + +} + +static void emit_load_member_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name, enum token_kind member_op, int offset) { + + if (sym) { + + if (member_op == TOK_DOT || sym->is_array) { + + if (sym->is_static && sym->static_label) { + emit_load_address_to_reg_now (reg, sym->static_label); + } else { + emit_load_local_address_to_reg_now (reg, sym->offset); + } + + } else if (sym->is_static && sym->static_label) { + emit_load_global_to_reg (reg, sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, sym->offset, DATA_PTR); + } + + } else { + + if (member_op == TOK_DOT || get_global_symbol_array (name)) { + emit_load_address_to_reg_now (reg, name); + } else { + emit_load_global_to_reg (reg, name, DATA_PTR); + } + + } + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + +} + +static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line) { + + int have_address = 0; + int have_direct_object_pointer = 0; + int last_elem_size = DATA_INT & 0x1f; + int last_lvalue_size = DATA_INT & 0x1f; + + const char *current_object_tag_name = 0; + int current_object_size = 0; + + postfix_copy_lvalue_size = DATA_INT & 0x1f; + + if (!reg) { + reg = "eax"; + } + + if (tok.kind == TOK_LPAREN) { + + if (src) { + return 0; + } + + ensure_global_function_symbol (src_name, name_start, name_caret, name_line); + emit_call_identifier_to_reg_now (src_name, reg, name_start, name_caret, name_line); + + have_address = 1; + have_direct_object_pointer = 1; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind postfix_op = tok.kind; + + int pointer_depth; + int pointed_size; + + get_token (); + + if (tok.kind != TOK_LBRACK) { + return 0; + } + + if (src) { + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + } else if (find_global_symbol (src_name) >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (src_name); + pointed_size = get_global_symbol_pointed_size (src_name); + + emit_load_global_to_reg (reg, src_name, DATA_PTR); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", src_name); + return 0; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer"); + return 0; + } + + last_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : (pointed_size > 0 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f)); + last_lvalue_size = last_elem_size; + + have_address = 1; + have_direct_object_pointer = 0; + + emit_incdec_symbol_now (src, src_name, postfix_op, name_line, name_start, name_caret); + + } + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) { + + if (tok.kind == TOK_LBRACK) { + + int elem_size = last_elem_size; + + if (!have_address) { + + if (src) { + + if (src->is_array) { + emit_load_symbol_address_for_copy_now (reg, src, src_name); + } else if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) : + (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size); + + } else { + + if (get_global_symbol_array (src_name)) { + emit_load_address_to_reg_now (reg, src_name); + } else { + emit_load_global_to_reg (reg, src_name, DATA_PTR); + } + + elem_size = get_global_symbol_array (src_name) ? + (get_global_symbol_pointer_depth (src_name) ? DATA_PTR : (get_global_symbol_array_element_size (src_name) > 0 ? get_global_symbol_array_element_size (src_name) : get_global_symbol_pointed_size (src_name))) : + (get_global_symbol_pointer_depth (src_name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (src_name)); + + } + + if ((elem_size & 0x1f) == 0) { + elem_size = DATA_INT & 0x1f; + } + + have_address = 1; + + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + last_elem_size = elem_size; + last_lvalue_size = elem_size; + + current_object_size = elem_size; + continue; + + } + + { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int member_is_array = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, + "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + return 0; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (current_object_size <= 0) { + + if (src) { + + if (member_op == TOK_ARROW && src->pointer_depth > 0) { + + current_object_size = src->pointed_size; + current_object_tag_name = src->pointed_tag_name; + + } else { + + current_object_size = src->size; + current_object_tag_name = src->tag_name; + + } + + } else { + + if (member_op == TOK_ARROW && get_global_symbol_pointer_depth (src_name) > 0) { + + current_object_size = get_global_symbol_pointed_size (src_name); + current_object_tag_name = get_global_symbol_tag_name (src_name); + + } else { + + current_object_size = get_global_symbol_size (src_name); + current_object_tag_name = get_global_symbol_tag_name (src_name); + + } + + } + + } + + if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, + "unknown member '%s'", member); + + free (member); + return 0; + + } + + { + + const char *member_tag_name = last_found_member_tag_name; + + if (member_is_array && member_pointer_depth > 0) { + + /* + * For an array member whose element type is a pointer, + * the subscript step is one pointer. Do not replace it + * with the pointed aggregate size from the member tag. + */ + member_elem_size = DATA_PTR; + + } else if (member_pointer_depth > 1) { + member_elem_size = DATA_PTR; + } else if (member_pointer_depth == 1 && member_tag_name) { + + struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0); + + if (entry) { + member_elem_size = entry->size; + } + + } + + current_object_tag_name = member_tag_name; + + } + + free (member); + + if (!have_address) { + + emit_load_member_address_for_copy_now (reg, src, src_name, member_op, 0); + have_address = 1; + + } else if (member_op == TOK_ARROW) { + + if (have_direct_object_pointer) { + have_direct_object_pointer = 0; + } else { + emit_load_deref_reg_now (reg, DATA_PTR); + } + + } else { + have_direct_object_pointer = 0; + } + + if (tok.kind == TOK_LBRACK && member_pointer_depth > 0 && !member_is_array) { + emit_load_member_from_addr_reg_now (reg, reg, member_offset, DATA_PTR & 0x1f); + } else if (state->ofp && member_offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, member_offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", member_offset, reg); + } + + } + + last_elem_size = member_elem_size; + last_lvalue_size = member_size; + + if (member_pointer_depth > 0 || member_is_array) { + current_object_size = member_elem_size; + } else { + current_object_size = member_size; + } + + } + + } + + postfix_copy_lvalue_size = last_lvalue_size; + return have_address; + +} + +static void emit_load_assignment_rhs_to_reg (const char *reg); + +static void emit_load_floating_deref_reg_now (const char *reg, int size) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld dword [%s]\n" : " fld dword ptr [%s]\n", reg); + } else { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld qword [%s]\n" : " fld qword ptr [%s]\n", reg); + } + + } else { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, " flds (%%%s)\n", reg); + } else { + fprintf (state->ofp, " fldl (%%%s)\n", reg); + } + + } + +} + +static void emit_load_floating_member_from_addr_reg_now (const char *reg, int offset, int size) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld dword [%s + %d]\n" : " fld dword ptr [%s + %d]\n", reg, offset); + } else { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld qword [%s + %d]\n" : " fld qword ptr [%s + %d]\n", reg, offset); + } + + } else { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, " flds %d(%%%s)\n", offset, reg); + } else { + fprintf (state->ofp, " fldl %d(%%%s)\n", offset, reg); + } + + } + +} + +static int expression_text_has_pluseq_before_delim_now (const char *p) { + + int depth = 0; + int quote; + + if (!p) { + return 0; + } + + while (*p) { + + if (depth == 0 && (*p == ',' || *p == ')' || *p == ';' || *p == ']' || *p == '}')) { + return 0; + } + + if (*p == '\'' || *p == '"') { + + quote = *p++; + + while (*p && *p != quote) { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p == quote) { + p++; + } + + continue; + + } + + if (p[0] == '+' && p[1] == '=') { + return 1; + } + + if (*p == '(' || *p == '[' || *p == '{') { + depth++; + } else if (*p == ')' || *p == ']' || *p == '}') { + + if (depth > 0) { + depth--; + } + + } + + p++; + + } + + return 0; + +} + +static int emit_parse_va_arg_address_to_reg_now (const char *reg, int size) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int64_s inc; + + int outer_paren = 0; + int deref_lvalue = 0; + + if (tok.kind == TOK_LPAREN) { + + outer_paren = 1; + get_token (); + + } + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + if (tok.kind == TOK_STAR) { + + deref_lvalue = 1; + get_token (); + + } + + if (tok.kind != TOK_IDENT) { + return 0; + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + expect (TOK_RPAREN, ")"); + + } else { + + if (tok.kind == TOK_STAR) { + + deref_lvalue = 1; + get_token (); + + } + + if (tok.kind != TOK_IDENT) { + return 0; + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + } + + if (tok.kind != TOK_PLUSEQ) { + + free (name); + return 0; + + } + + get_token (); + + if (token_is_sizeof_keyword ()) { + inc = sizeof_from_current_token (); + } else { + + inc.low = (unsigned long) size; + inc.high = 0; + + emit_load_assignment_rhs_to_reg (reg); + + } + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!src && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (deref_lvalue) { + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("edx", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [edx]\n" : " mov %s, dword ptr [edx]\n", reg); + } else { + fprintf (state->ofp, " movl (%%edx), %%%s\n", reg); + } + + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " add %s, %lu\n", reg, inc.low & U32_MASK); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [edx], %s\n" : " mov dword ptr [edx], %s\n", reg); + + } else { + + fprintf (state->ofp, " addl $%lu, %%%s\n", inc.low & U32_MASK, reg); + fprintf (state->ofp, " movl %%%s, (%%edx)\n", reg); + + } + + } + + } else { + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (reg, name, DATA_PTR); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %lu\n", reg, inc.low & U32_MASK); + } else { + fprintf (state->ofp, " addl $%lu, %%%s\n", inc.low & U32_MASK, reg); + } + + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_store_reg_to_global (src->static_label, DATA_PTR, reg); + } else { + emit_store_reg_to_local (src->offset, DATA_PTR, reg); + } + + } else { + emit_store_reg_to_global (name, DATA_PTR, reg); + } + + } + + if (tok.kind == TOK_COMMA) { + + get_token (); + + if (_accept (TOK_LPAREN)) { + + if (tok.kind == TOK_STAR) { + get_token (); + } + + if (tok.kind == TOK_IDENT) { + get_token (); + } + + expect (TOK_RPAREN, ")"); + + } else { + + if (tok.kind == TOK_STAR) { + get_token (); + } + + if (tok.kind == TOK_IDENT) { + get_token (); + } + + } + + if (tok.kind == TOK_MINUS) { + + get_token (); + + if (token_is_sizeof_keyword ()) { + (void) sizeof_from_current_token (); + } else { + skip_balanced_until (outer_paren ? TOK_RPAREN : TOK_SEMI, TOK_COMMA, TOK_EOF); + } + + } + + } + + if (outer_paren) { + expect (TOK_RPAREN, ")"); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " sub %s, %lu\n", reg, inc.low & U32_MASK); + } else { + fprintf (state->ofp, " subl $%lu, %%%s\n", inc.low & U32_MASK, reg); + } + + } + + free (name); + return 1; + +} + +static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg) { + + const char *p; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int current_object_size; + + const char *current_tag_name; + + int first_member; + int last_member_size; + int last_member_pointer_depth; + int last_member_elem_size; + + if (tok.kind != TOK_STAR || !tok.caret) { + return 0; + } + + p = tok.caret + 1; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '.' && !(*p == '-' && p[1] == '>')) { + return 0; + } + + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + current_object_size = src->pointed_size; + current_tag_name = src->pointed_tag_name; + + } else if (global_index >= 0) { + + emit_load_global_to_reg (reg, name, DATA_PTR); + + current_object_size = get_global_symbol_pointed_size (name); + current_tag_name = 0; + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + free (name); + + emit_load_deref_reg_now (reg, DATA_PTR); + first_member = 1; + + last_member_size = DATA_INT & 0x1f; + last_member_pointer_depth = 0; + last_member_elem_size = DATA_INT & 0x1f; + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int member_is_array = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, current_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + return 1; + + } + + free (member); + + if (!first_member && member_op == TOK_ARROW) { + emit_load_deref_reg_now (reg, DATA_PTR); + } + + if (member_offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, member_offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", member_offset, reg); + } + + } + + current_object_size = member_size; + current_tag_name = last_found_member_tag_name; + + last_member_size = member_size; + last_member_pointer_depth = member_pointer_depth; + last_member_elem_size = member_elem_size; + + first_member = 0; + + } + + emit_load_deref_reg_now (reg, last_member_size); + + if (last_member_pointer_depth > 0) { + set_rhs_last_pointer_info (last_member_pointer_depth, last_member_elem_size); + } else { + clear_rhs_last_pointer_info (); + } + + return 1; + +} + +static int source_starts_parenthesized_deref_postfix_incdec_subscript_at (const char *p) { + + if (!p || *p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) { + return 0; + } + + p += 2; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return 1; + +} + +static int source_starts_lparen_deref_subscript_at (const char *p) { + + if (!p || *p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '['; + +} + +static int source_starts_lparen_deref_postfix_incdec_at (const char *p) { + + if (!p) { + return 0; + } + + /* + * Most callers pass the caret at the opening parenthesis, but some + * token paths leave it just after the '('. Accept both positions so + * a statement such as: + * + * (*parameter_count)++; + * + * is always routed through the dereferenced-object inc/dec emitter, not + * the ordinary pointer-variable inc/dec path. The latter scales by the + * pointed-to size and would emit addl $4 for int *, corrupting counts. + */ + if (*p == '(') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + return source_starts_parenthesized_deref_postfix_incdec_subscript_at (p); + +} + +static int emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (const char *reg) { + + enum token_kind op; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int elem_size = DATA_INT & 0x1f; + int lvalue_size = DATA_INT & 0x1f; + int step = 1; + + const char *addr_reg; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (!source_starts_parenthesized_deref_postfix_incdec_subscript_at (tok.caret)) { + return 0; + } + + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + if (tok.kind != TOK_RPAREN) { + + free (name); + return 0; + + } + + get_token (); + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + } else if (global_index >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name); + + free (name); + return 1; + + } + + if (pointed_size <= 0) { + pointed_size = DATA_INT & 0x1f; + } + + if (pointer_depth > 1) { + + lvalue_size = DATA_PTR & 0x1f; + + elem_size = (pointer_depth > 2) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size); + step = elem_size; + + } else { + + lvalue_size = pointed_size & 0x1f; + + elem_size = pointed_size & 0x1f; + step = 1; + + } + + if (!reg) { + reg = "eax"; + } + + addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx"; + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (addr_reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (addr_reg, src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (addr_reg, name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size); + emit_push_reg_now (reg); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg); + } + + } + + emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size); + emit_pop_reg_now (reg); + + if (tok.kind == TOK_LBRACK) { + emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, pointer_depth - 1, pointed_size); + } + + if (pointer_depth > 1) { + set_rhs_last_pointer_info (pointer_depth - 1, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return 1; + +} + +static int source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (const char *p) { + + if (!p) { + return 0; + } + + if (*p == '*') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) { + return 0; + } + + p += 2; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '='; + +} + +static int source_starts_deref_parenthesized_deref_postfix_incdec_value_at (const char *p) { + + if (!p || *p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != ')') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) { + return 0; + } + + p += 2; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == ')'; + +} + +static int emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (const char *reg) { + + enum token_kind op; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int deref_size = DATA_INT & 0x1f; + int step = DATA_PTR & 0x1f; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (!source_starts_deref_parenthesized_deref_postfix_incdec_value_at (tok.caret)) { + return 0; + } + + expect (TOK_STAR, "*"); + expect (TOK_LPAREN, "("); + expect (TOK_LPAREN, "("); + expect (TOK_STAR, "*"); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + expect (TOK_RPAREN, ")"); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + } else if (global_index >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name); + + free (name); + return 1; + + } + + if (pointed_size <= 0) { + pointed_size = DATA_INT & 0x1f; + } + + deref_size = pointer_depth > 1 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f); + step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size); + + if (deref_size <= 0) { + deref_size = DATA_INT & 0x1f; + } + + if (step <= 0) { + step = DATA_PTR & 0x1f; + } + + if (!reg) { + reg = "eax"; + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now (reg, "ecx", 0, DATA_PTR & 0x1f); + emit_push_reg_now (reg); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg); + } + + } + + emit_store_reg_to_deref_reg_now ("ecx", reg, DATA_PTR & 0x1f); + emit_pop_reg_now (reg); + emit_load_deref_reg_now (reg, deref_size); + + if (pointer_depth > 2) { + set_rhs_last_pointer_info (pointer_depth - 2, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return 1; + +} + +static int emit_store_to_deref_parenthesized_deref_postfix_incdec_now (const char *reg) { + + enum token_kind op; + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int store_size = DATA_INT & 0x1f; + int step = DATA_PTR & 0x1f; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (!source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (tok.caret)) { + return 0; + } + + get_token (); + + expect (TOK_LPAREN, "("); + expect (TOK_STAR, "*"); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + expect (TOK_ASSIGN, "="); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + } else if (global_index >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name); + + free (name); + return 1; + + } + + if (pointed_size <= 0) { + pointed_size = DATA_INT & 0x1f; + } + + store_size = pointed_size & 0x1f; + step = pointed_size & 0x1f; + + if (step <= 0) { + step = DATA_PTR & 0x1f; + } + + if (!reg) { + reg = "eax"; + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now ("edx", "ecx", 0, DATA_PTR & 0x1f); + emit_push_reg_now ("edx"); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add edx, %d\n" : " sub edx, %d\n", step); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%edx\n" : " subl $%d, %%edx\n", step); + } + + } + + emit_store_reg_to_deref_reg_now ("ecx", "edx", DATA_PTR & 0x1f); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, store_size); + + free (name); + return 1; + +} + +static void emit_load_assignment_rhs_to_reg (const char *reg) { + + clear_rhs_last_pointer_info (); + + if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) { + + get_token (); + + if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) { + return; + } + + } + + /* + * Do not special-case parenthesized assignment expressions here. + * + * The normal parenthesized-expression path below already recurses back + * into emit_load_assignment_rhs_expression_to_reg(), and the identifier + * operand parser below already _accepts assignment operators. Using the + * old source-text detector here was wrong for nested grouping such as: + * + * ((((reta <<= 1)) & 0x10)) + * + * because it greedily treated all leading '(' tokens as belonging to the + * assignment expression, including parentheses that actually enclose the + * later binary '&' expression. + */ + if (_accept (TOK_LPAREN)) { + + char *paren_call_name = 0; + + const char *paren_call_start = 0; + const char *paren_call_caret = 0; + + unsigned long paren_call_line = 0; + + /* + * Function pointer call designator: (*fp)(args). + * + * If this is left to the normal parenthesized-expression path, the + * inner unary * path treats *fp as a data dereference and emits: + * + * movl off(%ebp), %eax + * movl (%eax), %eax + * + * That is correct for reading *p as an object, but wrong for a + * function designator. In a call, the value of fp is already the + * target address, so load fp and call through it without the extra + * data load. Keep this narrow: only handle the exact token form + * (*identifier)(...), and let every other parenthesized expression use + * the existing parser. + */ + if (tok.kind == TOK_STAR && tok.caret) { + + const char *p = tok.caret + 1; + const char *name_start; + const char *name_caret; + + unsigned long name_line; + char *name; + + struct local_symbol *src; + + int global_index; + int looks_like_call = 0; + + if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) { + return; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == ')') { + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + looks_like_call = (*p == '('); + + } + + } + + if (looks_like_call) { + + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg (reg, name, DATA_PTR); + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return; + + } + + free (name); + + emit_call_pointer_in_reg_now (reg, reg); + clear_rhs_last_pointer_info (); + + return; + + } + + } + + if (emit_load_parenthesized_indirect_member_to_reg_now (reg)) { + return; + } + + if (tok.kind == TOK_IDENT && tok.ident && + + find_global_symbol (tok.ident) >= 0 && + get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) { + + paren_call_name = xstrdup (tok.ident); + paren_call_start = tok.start; + paren_call_caret = tok.caret; + paren_call_line = get_line_number (); + + } + + if (!is_type_start (tok.kind) && parenthesized_function_designator_call_now ()) { + + char *call_name = xstrdup (tok.ident); + + const char *call_start = tok.start; + const char *call_caret = tok.caret; + + unsigned long call_line = get_line_number (); + get_token (); + + expect (TOK_RPAREN, ")"); + + if (!find_local_symbol (call_name)) { + ensure_global_function_symbol (call_name, call_start, call_caret, call_line); + } + + emit_call_identifier_to_reg_now (call_name, reg, call_start, call_caret, call_line); + free (call_name); + + return; + + } + + if (is_type_start (tok.kind)) { + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_inline = parsed_type_is_inline; + int saved_field_count = parsed_field_count; + int saved_fields[MAX_AGG_FIELDS]; + int saved_declarator_is_pointer = declarator_is_pointer; + int saved_declarator_pointer_depth = declarator_pointer_depth; + int saved_declarator_has_array = declarator_has_array; + int saved_declarator_has_function = declarator_has_function; + int saved_declarator_array_unsized = declarator_array_unsized; + long saved_declarator_array_count = declarator_array_count; + + char *cast_name = 0; + + int cast_base_size; + int cast_is_unsigned = 0; + int cast_pointer_depth = 0; + int cast_pointed_size = 0; + + int i; + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; + + parse_type_spec (); + + cast_base_size = parsed_type_size & 0x1f; + cast_is_unsigned = parsed_type_is_unsigned; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&cast_name); + } + + if (declarator_is_pointer) { + + cast_pointer_depth = declarator_pointer_depth > 0 ? declarator_pointer_depth : 1; + cast_pointed_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_base_size; + + if (cast_pointed_size <= 0) { + cast_pointed_size = DATA_INT & 0x1f; + } + + } + + if (cast_name) { + free (cast_name); + } + + expect (TOK_RPAREN, ")"); + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + clear_parsed_fields (); + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + parsed_field_sizes[i] = saved_fields[i]; + } + + parsed_field_count = saved_field_count; + + declarator_is_pointer = saved_declarator_is_pointer; + declarator_pointer_depth = saved_declarator_pointer_depth; + declarator_has_array = saved_declarator_has_array; + declarator_has_function = saved_declarator_has_function; + declarator_array_unsized = saved_declarator_array_unsized; + declarator_array_count = saved_declarator_array_count; + + emit_load_assignment_rhs_to_reg (reg); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now (reg); + } + + if (tok.kind == TOK_LBRACK) { + + int cast_subscript_elem_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_pointed_size; + + if (cast_subscript_elem_size <= 0) { + cast_subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, cast_subscript_elem_size); + emit_load_deref_reg_now (reg, cast_subscript_elem_size); + + } + + if (cast_pointer_depth > 0) { + set_rhs_last_pointer_info (cast_pointer_depth, cast_pointed_size); + } else { + + emit_apply_integer_cast_to_reg_now (reg, cast_base_size, cast_is_unsigned); + clear_rhs_last_pointer_info (); + + } + + return; + + } + + if (const_integer_expr_text_is_foldable_now (tok.caret)) { + + int64_s v = const64_from_current_foldable_expr (); + expect (TOK_RPAREN, ")"); + + emit_load_const32_to_reg_now (reg, v); + return; + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + + if (postfix_member_seen && (tok.kind == TOK_INCR || tok.kind == TOK_DECR)) { + + enum token_kind postfix_op = tok.kind; + get_token (); + emit_apply_postfix_member_incdec_now (reg, postfix_op); + + } + + while (tok.kind == TOK_COMMA) { + + get_token (); + emit_load_assignment_rhs_expression_to_reg (reg); + + } + + if (is_assignment_operator (tok.kind)) { + + get_token (); + emit_load_assignment_rhs_expression_to_reg (reg); + + } + + if (paren_call_name && tok.kind == TOK_RPAREN) { + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (paren_call_name)) { + ensure_global_function_symbol (paren_call_name, paren_call_start, paren_call_caret, paren_call_line); + } + + emit_call_identifier_to_reg_now (paren_call_name, reg, paren_call_start, paren_call_caret, paren_call_line); + free (paren_call_name); + + return; + + } + + } else { + expect (TOK_RPAREN, ")"); + } + + if (paren_call_name) { + free (paren_call_name); + } + + if (tok.kind == TOK_LBRACK) { + + int subscript_pointer_depth = rhs_last_pointer_depth; + int subscript_elem_size = rhs_last_pointed_size; + + if (subscript_elem_size <= 0) { + subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + get_token (); + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) && + tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + + unsigned long rhs_line = get_line_number (); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + set_rhs_last_pointer_info (0, 0); + return; + + } else if (assign_op == TOK_ASSIGN) { + + if (subscript_elem_size > (DATA_LLONG & 0x1f)) { + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + emit_load_assignment_rhs_expression_to_reg (reg); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + set_rhs_last_pointer_info (0, 0); + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + + } else { + + emit_load_deref_reg_now (reg, subscript_elem_size); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + + emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size); + set_rhs_last_pointer_info (0, 0); + + return; + + } + + emit_load_deref_reg_now (reg, subscript_elem_size); + + if (subscript_pointer_depth > 0) { + subscript_pointer_depth--; + } + + if (subscript_pointer_depth > 0) { + set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size); + } else { + set_rhs_last_pointer_info (0, 0); + } + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now (reg); + } + + if (postfix_member_seen && tok.kind == TOK_LPAREN) { + + emit_call_pointer_in_reg_now (reg, reg); + + set_rhs_last_pointer_info (0, 0); + return; + + } + + if (postfix_member_seen) { + + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind op = tok.kind; + + int assign_member_offset = postfix_member_offset; + int assign_member_size = postfix_member_size; + + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + if (postfix_member_is_floating || token_is_floating_constant_now ()) { + + emit_push_reg_now ("edx"); + emit_load_floating_rhs_expression_now (assign_member_size); + + emit_pop_reg_now ("edx"); + emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size); + + return; + + } + + if (assign_member_size > (DATA_LLONG & 0x1f) && + tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + + unsigned long rhs_line = get_line_number (); + emit_push_reg_now ("edx"); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = assign_member_offset; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + return; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) { + return; + } + + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + + } else { + + /* + * emit_apply_postfix_member_access_to_reg_now() has already + * loaded the member value into reg and left the containing + * object address in edx. Do not load the member again from + * reg: for p->m |= x that treats the old value of m as a + * pointer and dereferences it, which corrupts/segfaults code + * such as section->symbol->flags |= SYMBOL_FLAG_SECTION_SYMBOL. + */ + emit_push_reg_now ("edx"); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size); + + } else { + emit_load_assignment_rhs_expression_to_reg (reg); + } + + } + + } + + /* + * This routine parses one primary operand for the outer binary + * expression parser. After returning, the caller can still consume + * any trailing operator, e.g. the / b in: + * + * (a + b - 1) / b + */ + return; + + } else if (tok.kind == TOK_AMPER) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + if (emit_load_address_of_parenthesized_postfix_to_reg_now (reg)) { + return; + } + + } + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + int64_s zero; + + zero.low = 0; + zero.high = 0; + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after '&'"); + + emit_load_const32_to_reg_now (reg, zero); + return; + + } + + name = xstrdup (tok.ident); + get_token (); + + src = find_local_symbol (name); + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + int global_index = find_global_symbol (name); + + const char *current_object_tag_name = 0; + int current_object_size = 0; + + if (src) { + + if (tok.kind == TOK_DOT || src->is_array) { + + current_object_size = src->is_array && src->pointed_size > 0 ? src->pointed_size : src->size; + current_object_tag_name = src->is_array ? src->pointed_tag_name : 0; + + if (src->is_static && src->static_label) { + emit_load_address_to_reg_now (reg, src->static_label); + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + } else if (src->is_static && src->static_label) { + + current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR; + current_object_tag_name = src->pointed_tag_name; + + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + + } else { + + current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR; + current_object_tag_name = src->pointed_tag_name; + + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + + } + + } else if (global_index >= 0) { + + if (tok.kind == TOK_DOT || get_global_symbol_array (name)) { + + current_object_size = get_global_symbol_array (name) && get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name); + current_object_tag_name = get_global_symbol_tag_name (name); + + emit_load_address_to_reg_now (reg, name); + + } else { + + current_object_size = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : DATA_PTR; + current_object_tag_name = get_global_symbol_tag_name (name); + + emit_load_global_to_reg (reg, name, DATA_PTR); + + } + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return; + + } + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int member_size = DATA_INT & 0x1f; + int elem_size = DATA_INT & 0x1f; + int pointer_depth = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (name); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &elem_size, &pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return; + + } + + current_object_tag_name = last_found_member_tag_name; + free (member); + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth > 0) { + + emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f); + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + } else { + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + } + + } else if (pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) { + emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f); + } + + if (pointer_depth > 0) { + current_object_size = elem_size > 0 ? elem_size : DATA_PTR; + } else { + current_object_size = member_size; + } + + } + + free (name); + return; + + } + + if (tok.kind == TOK_LBRACK) { + + if (src) { + + int elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) : + (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size); + + if (elem_size <= 0) { + elem_size = DATA_INT & 0x1f; + } + + if (src->is_array) { + + if (src->is_static && src->static_label) { + emit_load_address_to_reg_now (reg, src->static_label); + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + } else if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + const char *current_object_tag_name = src->pointed_tag_name; + int current_object_size = elem_size; + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (name); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return; + + } + + current_object_tag_name = last_found_member_tag_name; + free (member); + + if (state->ofp && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg); + } + + } + + if (tok.kind == TOK_LBRACK) { + + if (member_pointer_depth > 0) { + emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f); + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, member_elem_size > 0 ? member_elem_size : DATA_INT & 0x1f); + + } else if (member_pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) { + emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f); + } + + if (member_pointer_depth > 0) { + current_object_size = member_elem_size > 0 ? member_elem_size : DATA_PTR; + } else { + current_object_size = member_size; + } + + } + + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + int elem_size = get_global_symbol_array (name) ? + (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) : + (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name)); + + if (elem_size <= 0) { + elem_size = DATA_INT & 0x1f; + } + + if (get_global_symbol_array (name)) { + emit_load_address_to_reg_now (reg, name); + } else { + emit_load_global_to_reg (reg, name, DATA_PTR); + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + free (name); + return; + + } + + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_address_to_reg_now (reg, src->static_label); + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + emit_load_address_to_reg_now (reg, name); + + free (name); + return; + + } + + { + + int64_s zero; + + zero.low = 0; + zero.high = 0; + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + emit_load_const32_to_reg_now (reg, zero); + + } + + return; + + } else if (tok.kind == TOK_STAR) { + + int deref_had_parens = 0; + int deref_size = DATA_CHAR & 0x1f; + int deref_pointer_depth = 0; + int deref_pointed_size = 0; + + if (emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (reg)) { + return; + } + + if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now (reg)) { + return; + } + + get_token (); + + if (tok.kind == TOK_IDENT) { + + char *lhs_name = xstrdup (tok.ident); + + const char *lhs_start = tok.start; + const char *lhs_caret = tok.caret; + + unsigned long lhs_line = get_line_number (); + struct local_symbol *lhs_sym; + + enum token_kind lhs_postfix_op = TOK_EOF; + enum token_kind member_op = TOK_EOF; + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int lhs_has_postfix = 0; + int offset = 0; + int member_size = DATA_PTR & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + + get_token (); + + if (tok.kind == TOK_LPAREN && + find_global_symbol (lhs_name) >= 0 && + get_global_symbol_kind (lhs_name) == GLOBAL_SYMBOL_FUNCTION) { + + int fptr_depth = get_global_symbol_pointer_depth (lhs_name); + int fpointed_size = get_global_symbol_pointed_size (lhs_name); + + emit_call_identifier_to_reg_now (lhs_name, reg, lhs_start, lhs_caret, lhs_line); + + if (fptr_depth > 1) { + deref_size = DATA_PTR; + } else if (fptr_depth == 1 && fpointed_size > 0) { + deref_size = fpointed_size; + } + + emit_load_deref_reg_now (reg, deref_size); + + free (lhs_name); + return; + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + member_op = tok.kind; + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (lhs_name); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (lhs_name); + + return; + + } + + free (member); + + if (member_pointer_depth > 1) { + deref_size = DATA_PTR; + } else if (member_pointer_depth == 1 && member_elem_size > 0) { + deref_size = member_elem_size; + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + lhs_has_postfix = 1; + lhs_postfix_op = tok.kind; + + get_token (); + + } + + if (tok.kind == TOK_ASSIGN) { + + lhs_sym = find_local_symbol (lhs_name); + + if (lhs_sym) { + + if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR); + } + + } else if (find_global_symbol (lhs_name) >= 0) { + emit_load_global_to_reg ("edx", lhs_name, DATA_PTR); + } else { + report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov ecx, dword [edx + %d]\n", offset); + + if (lhs_has_postfix) { + fprintf (state->ofp, " %s dword [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset); + } + + } else { + + fprintf (state->ofp, " mov ecx, dword ptr [edx + %d]\n", offset); + + if (lhs_has_postfix) { + fprintf (state->ofp, " %s dword ptr [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset); + } + + } + + } else { + + fprintf (state->ofp, " movl %d(%%edx), %%ecx\n", offset); + + if (lhs_has_postfix) { + fprintf (state->ofp, " %sl %d(%%edx)\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset); + } + + } + + } + + emit_push_reg_now ("ecx"); + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, deref_size); + + free (lhs_name); + return; + + } + + lhs_sym = find_local_symbol (lhs_name); + + if (lhs_sym) { + + if (member_op == TOK_DOT) { + + if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_address_to_reg_now (reg, lhs_sym->static_label); + } else { + emit_load_local_address_to_reg_now (reg, lhs_sym->offset); + } + + } else if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR); + } + + } else if (find_global_symbol (lhs_name) >= 0) { + + if (member_op == TOK_DOT) { + emit_load_address_to_reg_now (reg, lhs_name); + } else { + emit_load_global_to_reg (reg, lhs_name, DATA_PTR); + } + + } else { + report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", reg, reg, offset); + } + + } else { + fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg); + } + + } + + if (lhs_has_postfix) { + /* Already applied above only for assignment forms. */ + } + + emit_load_deref_reg_now (reg, deref_size); + + free (lhs_name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + lhs_has_postfix = 1; + lhs_postfix_op = tok.kind; + + get_token (); + + } + + if (tok.kind == TOK_ASSIGN) { + + int lhs_deref_is_unsigned = 1; + lhs_sym = find_local_symbol (lhs_name); + + if (lhs_sym) { + + if (lhs_sym->pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (lhs_sym->pointer_depth == 1 && lhs_sym->pointed_size > 0) { + + /** + * Keep the full pointed-to object size here. This + * assignment path also handles aggregate lvalues such + * as *hashtab = old_hashtab; Masking with 0x1f turns + * a 44-byte struct hashtab into 12 and emits a partial + * copy, corrupting pdas hashtab state during rehash(). + */ + deref_size = lhs_sym->pointed_size; + + } + + if (member_op == TOK_DOT) { + + if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_address_to_reg_now ("edx", lhs_sym->static_label); + } else { + emit_load_local_address_to_reg_now ("edx", lhs_sym->offset); + } + + } else if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR); + } + + } else if (find_global_symbol (lhs_name) >= 0) { + + if (get_global_symbol_pointer_depth (lhs_name) > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (lhs_name) == 1 && get_global_symbol_pointed_size (lhs_name) > 0) { + deref_size = get_global_symbol_pointed_size (lhs_name); + } + + if (member_op == TOK_DOT) { + emit_load_address_to_reg_now ("edx", lhs_name); + } else { + emit_load_global_to_reg ("edx", lhs_name, DATA_PTR); + } + + } else { + report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name); + } + + emit_push_reg_now ("edx"); + + if (lhs_has_postfix) { + emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret); + } + + get_token (); + + if (deref_size > (DATA_LLONG & 0x1f)) { + + emit_pop_reg_now ("edx"); + + if (!emit_store_assignment_to_aggregate_address_now ("edx", deref_size, lhs_start, lhs_caret, lhs_line)) { + report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "aggregate assignment expression not implemented"); + } + + } else if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_deref_is_unsigned); + emit_pop_reg_now ("ecx"); + + emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx"); + + } else { + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + + emit_store_reg_to_deref_reg_now ("edx", reg, deref_size); + + } + + free (lhs_name); + return; + + } + + lhs_sym = find_local_symbol (lhs_name); + + if (lhs_sym) { + + if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR); + } + + if (lhs_has_postfix) { + + emit_push_reg_now (reg); + emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret); + emit_pop_reg_now (reg); + + } + + if (lhs_sym->pointer_depth > 1) { + deref_size = DATA_PTR; + } else if (lhs_sym->pointer_depth == 1) { + deref_size = lhs_sym->pointed_size; + } + + emit_load_deref_reg_now (reg, deref_size); + + if (lhs_sym->pointer_depth > 1) { + set_rhs_last_pointer_info (lhs_sym->pointer_depth - 1, lhs_sym->pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, lhs_sym->pointer_depth > 0 ? lhs_sym->pointer_depth - 1 : 0, lhs_sym->pointed_size)) { + + free (lhs_name); + return; + + } + + free (lhs_name); + return; + + } + + if (find_global_symbol (lhs_name) >= 0) { + + emit_load_global_to_reg (reg, lhs_name, DATA_PTR); + + if (lhs_has_postfix) { + + emit_push_reg_now (reg); + emit_incdec_symbol_now (0, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret); + emit_pop_reg_now (reg); + + } + + if (get_global_symbol_pointer_depth (lhs_name) > 1) { + deref_size = DATA_PTR; + } else if (get_global_symbol_pointer_depth (lhs_name) == 1) { + deref_size = get_global_symbol_pointed_size (lhs_name); + } + + emit_load_deref_reg_now (reg, deref_size); + + if (get_global_symbol_pointer_depth (lhs_name) > 1) { + set_rhs_last_pointer_info (get_global_symbol_pointer_depth (lhs_name) - 1, get_global_symbol_pointed_size (lhs_name)); + } else { + clear_rhs_last_pointer_info (); + } + + if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, get_global_symbol_pointer_depth (lhs_name) > 0 ? get_global_symbol_pointer_depth (lhs_name) - 1 : 0, get_global_symbol_pointed_size (lhs_name))) { + + free (lhs_name); + return; + + } + + free (lhs_name); + return; + + } + + report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name); + free (lhs_name); + + return; + + } + + if (tok.kind == TOK_LPAREN) { + + deref_had_parens = 1; + get_token (); + + if (is_type_start (tok.kind)) { + + if (parse_deref_cast_type_name (&deref_size)) { + + int handled_va_arg = 0; + + if (expression_text_has_pluseq_before_delim_now (tok.start)) { + handled_va_arg = emit_parse_va_arg_address_to_reg_now (reg, deref_size); + } + + if (!handled_va_arg) { + + /* + * This is the address operand of a casted dereference, + * e.g. *(size_t *)ptr. Do not parse a full binary + * expression here: in + * + * *(size_t *)ptr + sizeof(size_t) + * + * the + belongs to the outer expression after the + * dereference, not to the address being dereferenced. + */ + emit_load_assignment_rhs_to_reg (reg); + + } + + + /* + * parse_deref_cast_type_name() consumes the cast parentheses + * only. Do not consume a following ')' here: in expressions + * such as: + * + * __munmap(ptr, *(size_t *)ptr + sizeof(size_t)) + * + * that ')' belongs to the surrounding call. Consuming it + * here makes the caller report a false "expected )". + */ + + if (tok.kind == TOK_ASSIGN) { + + get_token (); + + emit_push_reg_now (reg); + emit_load_assignment_rhs_expression_to_reg (reg); + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, deref_size); + + return; + + } + + goto dereference_loaded_address; + + } + + } + + if (tok.kind == TOK_AMPER) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *dst; + + int global_index; + int dst_size; + + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind == TOK_IDENT) { + + name = xstrdup (tok.ident); + + get_token (); + expect (TOK_RPAREN, ")"); + + if (is_assignment_operator (tok.kind)) { + + get_token (); + emit_load_assignment_rhs_expression_to_reg (reg); + + dst = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (dst) { + + dst_size = dst->size; + + if (dst->is_static && dst->static_label) { + emit_store_reg_to_global (dst->static_label, dst_size, reg); + } else { + emit_store_reg_to_local (dst->offset, dst_size, reg); + } + + } else if (global_index >= 0) { + + dst_size = get_global_symbol_size (name); + emit_store_reg_to_global (name, dst_size, reg); + + } else { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + free (name); + return; + + } + + if (find_local_symbol (name) || find_global_symbol (name) >= 0) { + + dst = find_local_symbol (name); + + if (dst) { + + if (dst->is_static && dst->static_label) { + emit_load_address_to_reg_now (reg, dst->static_label); + } else { + emit_load_local_address_to_reg_now (reg, dst->offset); + } + + } else { + emit_load_address_to_reg_now (reg, name); + } + + free (name); + goto dereference_loaded_address; + + } + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + free (name); + + return; + + } + + } + + } + + if (deref_had_parens) { + + emit_load_assignment_rhs_expression_to_reg (reg); + + if (rhs_last_pointer_depth > 1) { + deref_size = DATA_PTR; + } else if (rhs_last_pointer_depth == 1) { + deref_size = rhs_last_pointed_size; + } + + expect (TOK_RPAREN, ")"); + + /* + * A parenthesized dereferenced function pointer is still a function + * designator when it is followed by an argument list. For example: + * + * (*generate_func)(outfile, pos) + * + * The inner expression already loaded the function pointer value into + * reg. Do not dereference that value as data before the call; call + * through it directly. + */ + if (tok.kind == TOK_LPAREN) { + + emit_call_pointer_in_reg_now (reg, reg); + + clear_rhs_last_pointer_info (); + return; + + } + + if (tok.kind == TOK_ASSIGN) { + + emit_push_reg_now (reg); + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, deref_size); + + return; + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + enum token_kind postfix_op = TOK_EOF; + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int member_size = DATA_PTR & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + return; + + } + + free (member); + + if (member_pointer_depth > 1) { + deref_size = DATA_PTR; + } else if (member_pointer_depth == 1 && member_elem_size > 0) { + deref_size = member_elem_size; + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_op = tok.kind; + get_token (); + + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov edx, dword [%s + %d]\n", reg, offset); + } else { + fprintf (state->ofp, " mov edx, dword ptr [%s + %d]\n", reg, offset); + } + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s dword [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } else { + fprintf (state->ofp, " %s dword ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } + + } + + } else { + + fprintf (state->ofp, " movl %d(%%%s), %%edx\n", offset, reg); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + fprintf (state->ofp, " %sl %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg); + } + + } + + } + + if (tok.kind == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, deref_size); + + return; + + } + + if (state->ofp && strcmp (reg, "edx") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, edx\n", reg); + } else { + fprintf (state->ofp, " movl %%edx, %%%s\n", reg); + } + + } + + goto dereference_loaded_address; + + } + + } else { + emit_load_assignment_rhs_to_reg (reg); + } + + dereference_loaded_address: + + deref_pointer_depth = rhs_last_pointer_depth; + deref_pointed_size = rhs_last_pointed_size; + + emit_load_deref_reg_now (reg, deref_size); + + if (deref_pointer_depth > 1) { + set_rhs_last_pointer_info (deref_pointer_depth - 1, deref_pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + if (tok.kind == TOK_LBRACK) { + + int subscript_pointer_depth = deref_pointer_depth > 0 ? deref_pointer_depth - 1 : 0; + int subscript_elem_size = subscript_pointer_depth > 1 ? (DATA_PTR & 0x1f) : deref_pointed_size; + + if (subscript_elem_size <= 0) { + subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscripts_to_reg_now (reg, subscript_elem_size, subscript_pointer_depth, deref_pointed_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + get_token (); + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) && + tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) { + + char *rhs_name = xstrdup (tok.ident); + + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + + unsigned long rhs_line = get_line_number (); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + free (rhs_name); + + set_rhs_last_pointer_info (0, 0); + return; + + } else if (assign_op == TOK_ASSIGN) { + + if (subscript_elem_size > (DATA_LLONG & 0x1f)) { + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + emit_load_assignment_rhs_expression_to_reg (reg); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + set_rhs_last_pointer_info (0, 0); + return; + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + + } else { + + emit_load_deref_reg_now (reg, subscript_elem_size); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + + emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size); + clear_rhs_last_pointer_info (); + + return; + + } + + emit_load_deref_reg_now (reg, subscript_elem_size); + + if (subscript_pointer_depth > 1) { + set_rhs_last_pointer_info (subscript_pointer_depth - 1, deref_pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + } + + return; + + } else if (tok.kind == TOK_TILDE) { + + get_token (); + emit_load_assignment_rhs_to_reg (reg); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " not %s\n", reg); + } else { + fprintf (state->ofp, " notl %%%s\n", reg); + } + + } + + return; + + } else if (tok.kind == TOK_XMARK) { + + get_token (); + emit_load_assignment_rhs_to_reg (reg); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test %s, %s\n", reg, reg); + + if (strcmp (reg, "eax") != 0) { + + fprintf (state->ofp, " setz al\n"); + fprintf (state->ofp, " movzx eax, al\n"); + fprintf (state->ofp, " mov %s, eax\n", reg); + + } else { + + fprintf (state->ofp, " setz al\n"); + fprintf (state->ofp, " movzx eax, al\n"); + + } + + } else { + + fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg); + fprintf (state->ofp, " setz %%al\n"); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (reg, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + } + + return; + + } else if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + if (emit_load_prefix_incdec_member_to_reg_now (reg)) { + return; + } + + if (emit_load_prefix_incdec_to_reg_now (reg)) { + return; + } + + } + + if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + int negate = 0; + + while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + if (tok.kind == TOK_MINUS) { + negate = !negate; + } + + get_token (); + + } + + emit_load_assignment_rhs_to_reg (reg); + + if (negate && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " neg %s\n", reg); + } else { + fprintf (state->ofp, " negl %%%s\n", reg); + } + + } + + return; + + } + + if (token_is_sizeof_keyword ()) { + + int64_s v = sizeof_from_current_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg); + } + + } + + return; + + } + + if (is_string_token ()) { + + char *label = emit_string_literal_global (); + + switch_section (SECTION_TEXT); + emit_load_address_to_reg_now (reg, label); + + free (label); + + /* + * A string literal is a primary expression and may still have + * postfix operators applied to it. In particular, macro-expanded + * string constants are commonly subscripted in calls, e.g. + * + * tebc (LINKAGE_EDITOR_PROGRAM_NAME[i]) + * + * where LINKAGE_EDITOR_PROGRAM_NAME expands to a string literal. + * The old path returned immediately after loading the literal + * address, leaving the '[' token for the caller and causing a false + * "expected )". Treat the literal as a char array here and consume + * any following subscripts. + */ + if (tok.kind == TOK_LBRACK) { + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, DATA_CHAR & 0x1f); + emit_load_deref_reg_now (reg, DATA_CHAR & 0x1f); + + set_rhs_last_pointer_info (0, 0); + + } else { + set_rhs_last_pointer_info (1, DATA_CHAR & 0x1f); + } + + return; + + } + + switch (tok.kind) { + + case TOK_CCHAR: case TOK_CINT: case TOK_CUINT: case TOK_CULONG: + case TOK_CLONG: case TOK_CLLONG: case TOK_CULLONG: case TOK_LCHAR: + { + + int64_s v; + + int trailing_op = 0; + size_t len = tok.ident ? strlen (tok.ident) : 0; + + v.high = tok.val.i.high; + v.low = tok.val.i.low; + + if (len > 1 && tok.ident[len - 1] == '+') { + trailing_op = TOK_PLUS; + } else if (len > 1 && tok.ident[len - 1] == '-') { + trailing_op = TOK_MINUS; + } + + if (trailing_op) { + + free (tok.ident); + + tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-"); + tok.kind = (enum token_kind) trailing_op; + + if (tok.caret) { + tok.caret--; + } + + } else { + get_token (); + } + + emit_load_const32_to_reg_now (reg, v); + return; + + } + + case TOK_CFLOAT: case TOK_CDOUBLE: case TOK_CLDOUBLE: + { + + int trailing_op = 0; + size_t len = tok.ident ? strlen (tok.ident) : 0; + int64_s v; + + v.high = 0; + v.low = tok.val.ld != 0.0L ? 1 : 0; + + if (len > 1 && tok.ident[len - 1] == '+') { + trailing_op = TOK_PLUS; + } else if (len > 1 && tok.ident[len - 1] == '-') { + trailing_op = TOK_MINUS; + } + + if (trailing_op) { + + free (tok.ident); + + tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-"); + tok.kind = (enum token_kind) trailing_op; + + if (tok.caret) { + tok.caret--; + } + + } else { + get_token (); + } + + emit_load_const32_to_reg_now (reg, v); + return; + + } + + default: + + break; + + } + + if (tok.kind == TOK_IDENT) { + + enum token_kind postfix_op = TOK_EOF; + char *name = xstrdup (tok.ident); + + const char *name_start = tok.start, *name_caret = tok.caret; + unsigned long name_line = get_line_number (); + + struct local_symbol *src; + int postfix_incdec = 0; + + get_token (); + + { + + int64_s enum_value; + + if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) { + + emit_load_const32_to_reg_now (reg, enum_value); + + set_rhs_last_pointer_info (0, DATA_INT & 0x1f); + free (name); + + return; + + } + + } + + if (tok.kind == TOK_LBRACK) { + + src = find_local_symbol (name); + + if (src) { + + int elem_size; + + { + + elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) : + (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size); + + if (src->is_array) { + + if (src->is_static && src->static_label) { + emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1); + } + + } else if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, src->pointer_depth, src->pointed_size); + postfix_copy_lvalue_size = index_step_size (elem_size); + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + + if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) { + + free (name); + return; + + } + + if (postfix_member_seen) { + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + } else { + set_rhs_last_pointer_info (src->pointer_depth > 0 ? src->pointer_depth - 1 : 0, src->pointed_size); + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + int elem_size; + + { + + elem_size = get_global_symbol_array (name) ? + (get_global_symbol_pointer_depth (name) ? DATA_PTR : get_global_symbol_pointed_size (name)) : + (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name)); + + if (get_global_symbol_array (name)) { + emit_load_symbol_address_to_reg_now (reg, name, 0, 0); + } else { + emit_load_global_to_reg (reg, name, DATA_PTR); + } + + emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name)); + postfix_copy_lvalue_size = index_step_size (elem_size); + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + + if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) { + + free (name); + return; + + } + + free (name); + + return; + + } + + } + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (name)) { + ensure_global_function_symbol (name, name_start, name_caret, name_line); + } + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name); + } + + emit_call_identifier_to_reg_now (name, reg, name_start, name_caret, name_line); + + if (tok.kind == TOK_LBRACK) { + + int subscript_pointer_depth = get_global_symbol_pointer_depth (name); + int subscript_elem_size = get_global_symbol_pointed_size (name); + + if (subscript_elem_size <= 0) { + subscript_elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + get_token (); + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN) { + + if (subscript_elem_size > (DATA_LLONG & 0x1f)) { + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + emit_load_assignment_rhs_expression_to_reg (reg); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + postfix_member_seen = 0; + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + + } else { + + emit_load_deref_reg_now (reg, subscript_elem_size); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_member_to_addr_reg_now ("edx", 0, reg, subscript_elem_size); + set_rhs_last_pointer_info (0, 0); + + free (name); + return; + + } + + emit_load_deref_reg_now (reg, subscript_elem_size); + + if (subscript_pointer_depth > 0) { + subscript_pointer_depth--; + } + + if (subscript_pointer_depth > 0) { + set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size); + } else { + set_rhs_last_pointer_info (0, 0); + } + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + + if (postfix_member_seen && tok.kind == TOK_LPAREN) { + + emit_call_pointer_in_reg_now (reg, reg); + + set_rhs_last_pointer_info (0, 0); + free (name); + + return; + + } + + if (postfix_member_seen) { + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + } + + free (name); + return; + + } + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + struct local_symbol *dst; + int global_index; + int dst_size; + int dst_is_floating; + + get_token (); + + dst = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!dst && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", reg, reg); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg); + } + + } + + free (name); + return; + + } + + dst_size = dst ? dst->size : get_global_symbol_size (name); + dst_is_floating = dst ? dst->is_floating : get_global_symbol_floating (name); + + if (dst_is_floating) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression not implemented"); + skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF); + + free (name); + return; + + } + + if (dst_size == (DATA_LLONG & 0x1f)) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "64-bit assignment expression not implemented"); + skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF); + + free (name); + return; + + } + + if (assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + if (dst) { + + if (dst->is_static && dst->static_label) { + emit_load_global_to_reg (reg, dst->static_label, dst->size); + } else { + emit_load_local_to_reg (reg, dst->offset, dst->size); + } + + } else { + emit_load_global_to_reg (reg, name, dst_size); + } + + emit_load_assignment_rhs_expression_to_reg ("edx"); + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%eax\n", reg); + } + + } + + emit_assignment_binary_op (assign_op, dst ? dst->is_unsigned : get_global_symbol_unsigned (name)); + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } else { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + } + + if (dst) { + + if (dst->is_static && dst->static_label) { + emit_store_reg_to_global (dst->static_label, dst->size, reg); + } else { + emit_store_reg_to_local (dst->offset, dst->size, reg); + } + + if (dst->is_array || dst->pointer_depth > 0) { + set_rhs_last_pointer_info (dst->is_array ? 1 : dst->pointer_depth, dst->is_array ? local_array_pointer_step_size (dst) : dst->pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + } else { + + emit_store_reg_to_global (name, dst_size, reg); + + if (get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0) { + set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name)); + } else { + clear_rhs_last_pointer_info (); + } + + } + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + src = find_local_symbol (name); + + if (postfix_incdec && tok.kind == TOK_LBRACK) { + + int pointer_depth = 0; + int pointed_size = 0; + int elem_size = DATA_INT & 0x1f; + int is_unsigned = 1; + int known = 0; + + if (src) { + + known = 1; + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + is_unsigned = src->is_unsigned; + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (reg, src->offset, DATA_PTR); + } + + } else if (find_global_symbol (name) >= 0) { + + known = 1; + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + is_unsigned = get_global_symbol_unsigned (name); + emit_load_global_to_reg (reg, name, DATA_PTR); + + } + + if (!known) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer"); + + free (name); + return; + + } + + if (pointer_depth > 1) { + elem_size = DATA_PTR & 0x1f; + } else if (pointed_size > 0) { + elem_size = pointed_size & 0x1f; + } + + emit_push_reg_now (reg); + + emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret); + emit_pop_reg_now (reg); + + emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + get_token (); + + emit_push_reg_now (reg); + + if (assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + emit_load_deref_reg_now (reg, elem_size); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, is_unsigned); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", reg, elem_size); + + clear_rhs_last_pointer_info (); + free (name); + + return; + + } + + emit_load_deref_reg_now (reg, elem_size); + + if (pointer_depth > 1) { + set_rhs_last_pointer_info (pointer_depth - 1, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return; + + } + + if (src) { + + if (src->is_array) { + + if (src->is_static && src->static_label) { + emit_load_address_to_reg_now (reg, src->static_label); + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + } else if (tok.kind == TOK_DOT) { + + if (src->is_static && src->static_label) { + emit_load_address_to_reg_now (reg, src->static_label); + } else { + emit_load_local_address_to_reg_now (reg, src->offset); + } + + } else if (src->is_static && src->static_label) { + emit_load_global_to_reg (reg, src->static_label, src->size); + } else { + emit_load_local_to_reg (reg, src->offset, src->size); + } + + if (src->pointer_depth > 0) { + + /* + * Keep the pointed-to aggregate information even when the + * arrow is not immediately visible. Macro expansions commonly + * parenthesize pointer operands, e.g. ((unknown)->type). + * The inner expression sees ')' after 'unknown', and the + * outer parenthesized path sees the later '->'. If we clear + * the tag here, that later member lookup falls back to an + * unrelated member named 'type' and emits offset 0 instead of + * the struct cpp_unknown offset. + */ + postfix_copy_lvalue_size = src->pointed_size; + postfix_copy_lvalue_tag_name = src->pointed_tag_name; + + } else { + + postfix_copy_lvalue_size = src->size; + postfix_copy_lvalue_tag_name = src->tag_name; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + + if (postfix_member_seen && tok.kind == TOK_LPAREN) { + + emit_call_pointer_in_reg_now (reg, reg); + + set_rhs_last_pointer_info (0, 0); + free (name); + + return; + + } + + if (emit_store_assignment_to_postfix_member_now (reg)) { + + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + if (postfix_incdec) { + + if (postfix_member_seen) { + emit_apply_postfix_member_incdec_now (reg, postfix_op); + } else { + + emit_push_reg_now (reg); + emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret); + emit_pop_reg_now (reg); + + } + + } + + if (postfix_member_seen) { + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + } else { + set_rhs_last_pointer_info (src->is_array ? 1 : src->pointer_depth, src->is_array ? local_array_pointer_step_size (src) : src->pointed_size); + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION || + get_global_symbol_array (name) || + (!get_global_symbol_pointer_depth (name) && + get_global_symbol_size (name) > (DATA_PTR & 0x1f)) || + tok.kind == TOK_DOT) { + emit_load_address_to_reg_now (reg, name); + } else { + emit_load_global_to_reg (reg, name, get_global_symbol_size (name)); + } + + if (tok.kind == TOK_ARROW && get_global_symbol_pointer_depth (name) > 0) { + + postfix_copy_lvalue_size = get_global_symbol_pointed_size (name); + postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name); + + } else { + + postfix_copy_lvalue_size = get_global_symbol_size (name); + postfix_copy_lvalue_tag_name = 0; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + + if (postfix_member_seen && tok.kind == TOK_LPAREN) { + + emit_call_pointer_in_reg_now (reg, reg); + + set_rhs_last_pointer_info (0, 0); + free (name); + + return; + + } + + if (emit_store_assignment_to_postfix_member_now (reg)) { + + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + if (postfix_incdec) { + + if (postfix_member_seen) { + emit_apply_postfix_member_incdec_now (reg, postfix_op); + } else { + + emit_push_reg_now (reg); + emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret); + emit_pop_reg_now (reg); + + } + + } + + if (postfix_member_seen) { + set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size); + } else { + set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name)); + } + + free (name); + return; + + } + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + free (name); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", reg, reg); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg); + } + + } + + return; + + } + + if (recover_unknown_rhs_identifier ()) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", reg, reg); + } else { + fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg); + } + + } + + return; + + } + + { + + int64_s v = const64_from_current_operand (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg); + } + + } + + } + +} + +static int local_array_pointer_step_size (const struct local_symbol *sym) { + + if (!sym || !sym->is_array) { + return 0; + } + + if (sym->array_element_size > 0) { + return index_step_size (sym->array_element_size); + } + + if (sym->pointed_size > 0 && sym->pointed_size < sym->size) { + return index_step_size (sym->pointed_size); + } + + return index_step_size (sym->size); + +} + +static int global_array_pointer_step_size (const char *name) { + + long count; + int pointed_size; + + if (!name || !get_global_symbol_array (name)) { + return 0; + } + + if (get_global_symbol_array_element_size (name) > 0) { + return get_global_symbol_array_element_size (name); + } + + count = get_global_symbol_array_count (name); + + if (count > 0) { + return (int) (get_global_symbol_size (name) / count); + } + + pointed_size = get_global_symbol_pointed_size (name); + + if (pointed_size > 0 && pointed_size < get_global_symbol_size (name)) { + return index_step_size (pointed_size); + } + + return index_step_size (get_global_symbol_size (name)); + +} + +static void emit_scale_reg_by_const_now (const char *reg, int scale) { + + if (!state->ofp || scale <= 1) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " imul %s, %d\n", reg, scale); + } else { + fprintf (state->ofp, " imull $%d, %%%s, %%%s\n", scale, reg, reg); + } + +} + +static void emit_divide_eax_by_const_now (int divisor) { + + if (!state->ofp || divisor <= 1) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov ecx, %d\n", divisor); + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idiv ecx\n"); + + } else { + + fprintf (state->ofp, " movl $%d, %%ecx\n", divisor); + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idivl %%ecx\n"); + + } + +} + +static void emit_assignment_binary_op (enum token_kind op, int is_unsigned) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " add eax, edx\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " sub eax, edx\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imul eax, edx\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " mov ecx, edx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor edx, edx\n"); + fprintf (state->ofp, " div ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idiv ecx\n"); + + } + + break; + + case TOK_MOD: + + fprintf (state->ofp, " mov ecx, edx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor edx, edx\n"); + fprintf (state->ofp, " div ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idiv ecx\n"); + + } + + fprintf (state->ofp, " mov eax, edx\n"); + break; + + case TOK_MODEQ: + + fprintf (state->ofp, " mov ecx, edx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor edx, edx\n"); + fprintf (state->ofp, " div ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idiv ecx\n"); + + } + + fprintf (state->ofp, " mov eax, edx\n"); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " and eax, edx\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " or eax, edx\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xor eax, edx\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + fprintf (state->ofp, " mov ecx, edx\n"); + fprintf (state->ofp, " shl eax, cl\n"); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + fprintf (state->ofp, " mov ecx, edx\n"); + fprintf (state->ofp, is_unsigned ? " shr eax, cl\n" : " sar eax, cl\n"); + + break; + + default: + + break; + + } + + } else { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " addl %%edx, %%eax\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " subl %%edx, %%eax\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imull %%edx, %%eax\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorl %%edx, %%edx\n"); + fprintf (state->ofp, " divl %%ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idivl %%ecx\n"); + + } + + break; + + case TOK_MOD: + + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorl %%edx, %%edx\n"); + fprintf (state->ofp, " divl %%ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idivl %%ecx\n"); + + } + + fprintf (state->ofp, " movl %%edx, %%eax\n"); + break; + + case TOK_MODEQ: + + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorl %%edx, %%edx\n"); + fprintf (state->ofp, " divl %%ecx\n"); + + } else { + + fprintf (state->ofp, " cdq\n"); + fprintf (state->ofp, " idivl %%ecx\n"); + + } + + fprintf (state->ofp, " movl %%edx, %%eax\n"); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " andl %%edx, %%eax\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " orl %%edx, %%eax\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xorl %%edx, %%eax\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + fprintf (state->ofp, " sall %%cl, %%eax\n"); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%eax\n" : " sarl %%cl, %%eax\n"); + + break; + + default: + + break; + + } + + } + +} + +static int token_is_floating_constant_now (void) { + return tok.kind == TOK_CFLOAT || tok.kind == TOK_CDOUBLE || tok.kind == TOK_CLDOUBLE; +} + +static int64_s floating_constant_to_bits_now (int size) { + + int64_s r; + + unsigned long bits32; + unsigned char bytes[8]; + + int i; + + r.low = 0; + r.high = 0; + + if (size == (DATA_FLOAT & 0x1f)) { + + float f; + + if (tok.kind == TOK_CFLOAT) { + f = tok.val.f; + } else if (tok.kind == TOK_CLDOUBLE) { + f = (float) tok.val.ld; + } else { + f = (float) tok.val.d; + } + + memcpy (&bits32, &f, sizeof (f)); + + r.low = bits32; + get_token (); + + return r; + + } + + { + + double d; + + if (tok.kind == TOK_CFLOAT) { + d = (double) tok.val.f; + } else if (tok.kind == TOK_CLDOUBLE) { + d = (double) tok.val.ld; + } else { + d = tok.val.d; + } + + memset (bytes, 0, sizeof (bytes)); + memcpy (bytes, &d, sizeof (d)); + + for (i = 0; i < 4; i++) { + r.low |= ((unsigned long) bytes[i]) << (i * 8); + } + + for (i = 0; i < 4; i++) { + r.high |= ((unsigned long) bytes[i + 4]) << (i * 8); + } + + get_token (); + return r; + + } + +} + +static long double floating_constant_to_ld_now (void) { + + long double v; + + if (tok.kind == TOK_CFLOAT) { + v = (long double) tok.val.f; + } else if (tok.kind == TOK_CLDOUBLE) { + v = tok.val.ld; + } else { + v = (long double) tok.val.d; + } + + get_token (); + return v; + +} + +static long double parse_floating_const_expr_value_now (void); +static long double parse_floating_const_term_now (void); +static long double parse_floating_const_primary_now (void); + +static long double parse_floating_const_primary_now (void) { + + long double v; + int64_s iv; + + if (_accept (TOK_LPAREN)) { + v = parse_floating_const_expr_value_now (); + expect (TOK_RPAREN, ")"); + return v; + } + + if (_accept (TOK_PLUS)) { + return parse_floating_const_primary_now (); + } + + if (_accept (TOK_MINUS)) { + return -parse_floating_const_primary_now (); + } + + if (token_is_floating_constant_now ()) { + return floating_constant_to_ld_now (); + } + + iv = const64_from_current_operand (); + v = (long double) iv.low; + + if (iv.high != 0) { + v += ((long double) iv.high) * 4294967296.0L; + } + + return v; + +} + +static long double parse_floating_const_term_now (void) { + + long double v; + enum token_kind op; + long double rhs; + + v = parse_floating_const_primary_now (); + + while (tok.kind == TOK_STAR || tok.kind == TOK_BSLASH) { + + op = tok.kind; + get_token (); + rhs = parse_floating_const_primary_now (); + + if (op == TOK_STAR) { + v *= rhs; + } else { + v /= rhs; + } + + } + + return v; + +} + +static long double parse_floating_const_expr_value_now (void) { + + long double v; + enum token_kind op; + long double rhs; + + v = parse_floating_const_term_now (); + + while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + op = tok.kind; + get_token (); + rhs = parse_floating_const_term_now (); + + if (op == TOK_PLUS) { + v += rhs; + } else { + v -= rhs; + } + + } + + return v; + +} + +static int64_s parse_floating_const_expr_bits_now (int size) { + + long double acc; + int64_s r; + + acc = parse_floating_const_expr_value_now (); + r.low = 0; + r.high = 0; + + if (size == (DATA_FLOAT & 0x1f)) { + + float f; + unsigned long bits32; + + f = (float) acc; + bits32 = 0; + memcpy (&bits32, &f, sizeof (f)); + r.low = bits32; + return r; + + } + + { + + double d; + unsigned char bytes[8]; + int i; + + d = (double) acc; + memset (bytes, 0, sizeof (bytes)); + memcpy (bytes, &d, sizeof (d)); + + for (i = 0; i < 4; i++) { + r.low |= ((unsigned long) bytes[i]) << (i * 8); + } + + for (i = 0; i < 4; i++) { + r.high |= ((unsigned long) bytes[i + 4]) << (i * 8); + } + + return r; + + } + +} + +static void emit_load_floating_const_bits_now (int size, int64_s v) { + + int lab; + + if (!state->ofp) { + return; + } + + lab = anon_label++; + + if (state->syntax & ASM_SYNTAX_MASM) { + + fprintf (state->ofp, ".data\n"); + fprintf (state->ofp, "LC%d_flt:\n", lab); + fprintf (state->ofp, " dd %lu\n", v.low & U32_MASK); + + if (size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " dd %lu\n", v.high & U32_MASK); + } + + fprintf (state->ofp, ".code\n"); + fprintf (state->ofp, " fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, "section .data\n"); + fprintf (state->ofp, "LC%d_flt:\n", lab); + fprintf (state->ofp, " dd %lu\n", v.low & U32_MASK); + + if (size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " dd %lu\n", v.high & U32_MASK); + } + + fprintf (state->ofp, "section .text\n"); + fprintf (state->ofp, " fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + fprintf (state->ofp, ".data\n"); + fprintf (state->ofp, ".LC%d_flt:\n", lab); + fprintf (state->ofp, " .long %lu\n", v.low & U32_MASK); + + if (size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " .long %lu\n", v.high & U32_MASK); + } + + fprintf (state->ofp, ".text\n"); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fld %s ptr .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + } else { + fprintf (state->ofp, " fld%s .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", lab); + } + + } + + } + +} + +static void emit_load_floating_symbol_now (struct local_symbol *sym, const char *name, int size) { + + char memref[64]; + const char *label; + const char *asm_name; + + if (!state->ofp) { + return; + } + + asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s [%s]\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label); + } else { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref); + + } + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s [%s]\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name); + } + + } else { + + if (sym) { + + label = (sym->is_static && sym->static_label) ? sym->static_label : 0; + + if (label) { + fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label); + } else { + fprintf (state->ofp, " fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset); + } + + } else { + fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name); + } + + } + +} + +static void emit_load_floating_member_symbol_now (struct local_symbol *sym, const char *name, int offset, int size) { + + char memref[64]; + char labelref[256]; + + const char *label; + const char *asm_name; + const char *opsize; + + if (!state->ofp) { + return; + } + + opsize = size == (DATA_FLOAT & 0x1f) ? "dword" : "qword"; + asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + + if (offset) { + + sprintf (labelref, "%s + %d", sym->static_label, offset); + label = labelref; + + } else { + label = sym->static_label; + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, label); + + } else { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, memref); + + } + + } else { + + if (offset) { + + sprintf (labelref, "%s + %d", asm_name, offset); + label = labelref; + + } else { + label = asm_name; + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, label); + + } + + } else { + + if (sym) { + + label = (sym->is_static && sym->static_label) ? sym->static_label : 0; + + if (label) { + + if (offset) { + + sprintf (labelref, "%s+%d", label, offset); + label = labelref; + + } + + fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label); + + } else { + fprintf (state->ofp, " fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset + offset); + } + + } else { + + if (offset) { + + sprintf (labelref, "%s+%d", asm_name, offset); + asm_name = labelref; + + } + + fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name); + + } + + } + +} + +static void emit_duplicate_floating_stack_top_now (void) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " fld st0\n"); + } else if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fld st(0)\n"); + } else { + fprintf (state->ofp, " fld %%st(0)\n"); + } + +} + +static void emit_store_floating_symbol_now (struct local_symbol *sym, const char *name, int size) { + + char memref[64]; + const char *label; + const char *asm_name; + + if (!state->ofp) { + return; + } + + asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label); + } else { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref); + + } + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name); + } + + } else { + + if (sym) { + + label = (sym->is_static && sym->static_label) ? sym->static_label : 0; + + if (label) { + fprintf (state->ofp, " fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label); + } else { + fprintf (state->ofp, " fstp%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset); + } + + } else { + fprintf (state->ofp, " fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name); + } + + } + +} + +static void emit_load_any_symbol_as_floating_now (struct local_symbol *src, const char *name, int size, int is_floating) { + + if (is_floating) { + + emit_load_floating_symbol_now (src, name, size); + return; + + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("eax", src->static_label, src->size); + } else { + emit_load_local_to_reg ("eax", src->offset, src->size); + } + + } else { + emit_load_global_to_reg ("eax", name, size); + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + +} + +static int emit_load_floating_prefix_incdec_now (void) { + + enum token_kind op; + char *name; + + const char *name_start, *name_caret; + unsigned long name_line; + + struct local_symbol *sym; + int size; + int is_floating; + + if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) { + return 0; + } + + if (!name) { + return 1; + } + + sym = find_local_symbol (name); + emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret); + + if (sym) { + + size = sym->size; + is_floating = sym->is_floating; + + emit_load_any_symbol_as_floating_now (sym, name, size, is_floating); + + } else if (find_global_symbol (name) >= 0) { + + size = get_global_symbol_size (name); + is_floating = get_global_symbol_floating (name); + + emit_load_any_symbol_as_floating_now (0, name, size, is_floating); + + } + + free (name); + return 1; + +} + +static void emit_load_floating_rhs_expression_now (int result_size); +static void emit_floating_binary_now (enum token_kind k); + +static int floating_assignment_operator_supported_now (enum token_kind op); +static int is_value_compare_operator (enum token_kind k); + +static void emit_statement_label (int label); +static void emit_statement_label_raw (int label); +static void emit_statement_jump (int label); + +static int floating_rhs_result_in_eax_bool = 0; + +static void emit_eax_bool_to_floating_stack_now (void) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + floating_rhs_result_in_eax_bool = 0; + +} + +static const char *floating_compare_true_setcc_now (enum token_kind op) { + + switch (op) { + + case TOK_LESS: + + return "setb"; + + case TOK_LTEQ: + + return "setbe"; + + case TOK_GREATER: + + return "seta"; + + case TOK_GTEQ: + + return "setae"; + + case TOK_EQEQ: + + return "sete"; + + case TOK_NOTEQ: + + return "setne"; + + default: + + return "setz"; + + } + +} + +static void emit_floating_compare_to_eax_now (enum token_kind op) { + + const char *setcc; + + if (!state->ofp) { + floating_rhs_result_in_eax_bool = 1; + return; + } + + setcc = floating_compare_true_setcc_now (op); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " fxch st1\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, " %s al\n", setcc); + fprintf (state->ofp, " movzx eax, al\n"); + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " fxch st(1)\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, " %s al\n", setcc); + fprintf (state->ofp, " movzx eax, al\n"); + + } else { + + fprintf (state->ofp, " fxch %%st(1)\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw %%ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, " %s %%al\n", setcc); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + } + + floating_rhs_result_in_eax_bool = 1; + +} + +static void emit_load_floating_rhs_operand_now (int result_size) { + + if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + enum token_kind unary_op = tok.kind; + get_token (); + + emit_load_floating_rhs_operand_now (result_size); + + if (unary_op == TOK_MINUS) { + fprintf (state->ofp, " fchs\n"); + } + + return; + + } + + if (tok.kind == TOK_STAR) { + + int deref_size = result_size; + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) { + + if (!expression_text_has_pluseq_before_delim_now (tok.start) || + !emit_parse_va_arg_address_to_reg_now ("eax", deref_size)) { + emit_load_assignment_rhs_to_reg ("eax"); + } + + emit_load_floating_deref_reg_now ("eax", deref_size); + return; + + } + + emit_load_assignment_rhs_expression_to_reg ("eax"); + expect (TOK_RPAREN, ")"); + + emit_load_floating_deref_reg_now ("eax", deref_size); + return; + + } + + emit_load_assignment_rhs_to_reg ("eax"); + emit_load_floating_deref_reg_now ("eax", deref_size); + + return; + + } + + if (_accept (TOK_LPAREN)) { + + if (token_starts_type_name ()) { + + int cast_size = 0; + int cast_is_unsigned = 0; + int cast_is_pointer = 0; + + if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) { + + emit_load_floating_rhs_operand_now (result_size); + return; + + } + + } + + emit_load_floating_rhs_expression_now (result_size); + expect (TOK_RPAREN, ")"); + + if (floating_rhs_result_in_eax_bool && tok.kind != TOK_QMARK) { + emit_eax_bool_to_floating_stack_now (); + } + + return; + + } + + if (emit_load_floating_prefix_incdec_now ()) { + return; + } + + if (token_is_sizeof_keyword ()) { + + int64_s v = sizeof_from_current_token (); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl $%lu, (%%esp)\n", v.low & U32_MASK); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + return; + + } + + if (tok.kind == TOK_IDENT) { + + char *name = xstrdup (tok.ident); + + const char *name_start = tok.start, *name_caret = tok.caret; + unsigned long name_line = get_line_number (); + + struct local_symbol *src = find_local_symbol (name); + get_token (); + + if (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + int dst_size = 0; + int dst_is_floating = 0; + int have_dst = 0; + + if (src) { + + dst_size = src->size; + + dst_is_floating = src->is_floating; + have_dst = 1; + + } else if (find_global_symbol (name) >= 0) { + + dst_size = get_global_symbol_size (name); + + dst_is_floating = get_global_symbol_floating (name); + have_dst = 1; + + } + + if (have_dst && dst_is_floating && floating_assignment_operator_supported_now (assign_op)) { + + get_token (); + + if (assign_op == TOK_ASSIGN) { + emit_load_floating_rhs_expression_now (dst_size); + } else { + + emit_load_floating_symbol_now (src, name, dst_size); + emit_load_floating_rhs_expression_now (dst_size); + + emit_floating_binary_now (assign_op); + + } + + emit_duplicate_floating_stack_top_now (); + emit_store_floating_symbol_now (src, name, dst_size); + + free (name); + return; + + } + + } + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (name)) { + ensure_global_function_symbol (name, name_start, name_caret, name_line); + } + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name); + } + + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + + if (!get_global_symbol_floating (name)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + } + + free (name); + return; + + } + + if (tok.kind == TOK_ARROW && (src || find_global_symbol (name) >= 0)) { + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = result_size; + int member_elem_size = result_size; + int member_pointer_depth = 0; + int member_is_array = 0; + int member_is_floating = 0; + int base_size; + + const char *base_tag_name; + + if (src) { + + base_size = src->pointed_size; + base_tag_name = src->pointed_tag_name; + + } else { + + base_size = get_global_symbol_pointed_size (name); + base_tag_name = get_global_symbol_tag_name (name); + + } + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after ->"); + + free (name); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, base_size, base_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, &member_is_floating)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return; + + } + + free (member); + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("eax", src->static_label, DATA_PTR & 0x1f); + } else { + emit_load_local_to_reg ("eax", src->offset, DATA_PTR & 0x1f); + } + + } else { + emit_load_global_to_reg ("eax", name, DATA_PTR & 0x1f); + } + + if (member_is_floating) { + emit_load_floating_member_from_addr_reg_now ("eax", member_offset, member_size); + } else { + + emit_load_member_from_addr_reg_now ("eax", "eax", member_offset, member_size); + emit_eax_bool_to_floating_stack_now (); + + } + + free (name); + return; + + } + + if (tok.kind == TOK_DOT && (src || find_global_symbol (name) >= 0)) { + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = result_size; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after ."); + + free (name); + return; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &member_offset, &member_size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return; + + } + + free (member); + + if (member_size == (DATA_FLOAT & 0x1f) || member_size == (DATA_DOUBLE & 0x1f)) { + emit_load_floating_member_symbol_now (src, name, member_offset, member_size); + } else { + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("eax", src->static_label, src->size); + } else { + emit_load_local_to_reg ("eax", src->offset, src->size); + } + + } else { + emit_load_global_to_reg ("eax", name, get_global_symbol_size (name)); + } + + emit_apply_postfix_member_access_to_reg_now ("eax"); + emit_eax_bool_to_floating_stack_now (); + + } + + free (name); + return; + + } + + if (src) { + + if (src->is_floating) { + emit_load_floating_symbol_now (src, name, src->size); + } else { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("eax", src->static_label, src->size); + } else { + emit_load_local_to_reg ("eax", src->offset, src->size); + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind postfix_op = tok.kind; + get_token (); + + emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret); + + } + + free (name); + return; + + } + + if (find_global_symbol (name) >= 0) { + + if (get_global_symbol_floating (name)) { + emit_load_floating_symbol_now (0, name, get_global_symbol_size (name)); + } else { + + emit_load_global_to_reg ("eax", name, get_global_symbol_size (name)); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], eax\n"); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], eax\n"); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%eax, (%%esp)\n"); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind postfix_op = tok.kind; + get_token (); + + emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret); + + } + + free (name); + return; + + } + + free (name); + + } + + if (token_is_floating_constant_now ()) { + + emit_load_floating_const_bits_now (result_size, floating_constant_to_bits_now (result_size)); + return; + + } + + if (recover_unknown_rhs_identifier ()) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fldz\n"); + } else { + fprintf (state->ofp, " fldz\n"); + } + return; + + } + + { + + int64_s v = const64_from_current_operand (); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [esp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword [esp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [esp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl $%lu, (%%esp)\n", v.low & U32_MASK); + fprintf (state->ofp, " fildl (%%esp)\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + + } + +} + +static int token_is_floating_binary_now (enum token_kind k) { + return k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH; +} + +static void emit_floating_binary_now (enum token_kind k) { + + const char *st1; + const char *st0; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + + st1 = "st1"; + st0 = "st0"; + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + st1 = "st(1)"; + st0 = "st(0)"; + + } else { + + st1 = "%st(1)"; + st0 = "%st"; + + } + + switch (k) { + + case TOK_PLUS: case TOK_PLUSEQ: + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " faddp %s, %s\n", st1, st0); + } else { + fprintf (state->ofp, " faddp %s, %s\n", st0, st1); + } + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fsubp %s, %s\n", st1, st0); + } else { + fprintf (state->ofp, " fsubp %s, %s\n", st0, st1); + } + break; + + case TOK_STAR: case TOK_STAREQ: + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fmulp %s, %s\n", st1, st0); + } else { + fprintf (state->ofp, " fmulp %s, %s\n", st0, st1); + } + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fdivp %s, %s\n", st1, st0); + } else { + fprintf (state->ofp, " fdivp %s, %s\n", st0, st1); + } + break; + + default: + + break; + + } + +} + +static void emit_scale_reg_for_pointer_compound_assignment_now (const char *reg, struct local_symbol *lhs, const char *name, enum token_kind op) { + + int pointer_depth; + int pointed_size; + int elem_size; + + if (op != TOK_PLUSEQ && op != TOK_MINUSEQ) { + return; + } + + pointer_depth = lhs ? lhs->pointer_depth : get_global_symbol_pointer_depth (name); + pointed_size = lhs ? lhs->pointed_size : get_global_symbol_pointed_size (name); + + if (pointer_depth <= 0) { + return; + } + + elem_size = pointer_depth > 1 ? DATA_PTR : pointed_size; + + if (elem_size > 1) { + emit_scale_reg_by_const_now (reg, elem_size); + } + +} + +static int floating_assignment_operator_supported_now (enum token_kind op) { + return op == TOK_ASSIGN || op == TOK_PLUSEQ || op == TOK_MINUSEQ || op == TOK_STAREQ || op == TOK_SLASHEQ; +} + +static void emit_load_floating_rhs_expression_now (int result_size) { + + enum token_kind op; + + int false_label; + int end_label; + + floating_rhs_result_in_eax_bool = 0; + emit_load_floating_rhs_operand_now (result_size); + + while (token_is_floating_binary_now (tok.kind)) { + + if (floating_rhs_result_in_eax_bool) { + emit_eax_bool_to_floating_stack_now (); + } + + op = tok.kind; + get_token (); + + emit_load_floating_rhs_operand_now (result_size); + + if (floating_rhs_result_in_eax_bool) { + emit_eax_bool_to_floating_stack_now (); + } + + emit_floating_binary_now (op); + + } + + if (is_value_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + emit_load_floating_rhs_operand_now (result_size); + + while (token_is_floating_binary_now (tok.kind)) { + + enum token_kind rhs_op = tok.kind; + get_token (); + + emit_load_floating_rhs_operand_now (result_size); + emit_floating_binary_now (rhs_op); + + } + + emit_floating_compare_to_eax_now (op); + + } + + if (tok.kind == TOK_QMARK) { + + false_label = anon_label++; + end_label = anon_label++; + + if (!floating_rhs_result_in_eax_bool) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " fstp dword [esp]\n"); + fprintf (state->ofp, " mov eax, dword [esp]\n"); + + } else { + + fprintf (state->ofp, " fstp dword ptr [esp]\n"); + fprintf (state->ofp, " mov eax, dword ptr [esp]\n"); + + } + + fprintf (state->ofp, " add esp, 4\n"); + fprintf (state->ofp, " test eax, eax\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " fstps (%%esp)\n"); + fprintf (state->ofp, " movl (%%esp), %%eax\n"); + fprintf (state->ofp, " addl $4, %%esp\n"); + fprintf (state->ofp, " testl %%eax, %%eax\n"); + + } + + } + + } else if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " test eax, eax\n"); + } else { + fprintf (state->ofp, " testl %%eax, %%eax\n"); + } + + } + + if (state->ofp) { + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, " jz L%d\n", false_label); + } else { + fprintf (state->ofp, " jz .L%d\n", false_label); + } + + } + + floating_rhs_result_in_eax_bool = 0; + get_token (); + + emit_load_floating_rhs_expression_now (result_size); + expect (TOK_COLON, ":"); + + emit_statement_jump (end_label); + emit_statement_label (false_label); + + emit_load_floating_rhs_expression_now (result_size); + emit_statement_label (end_label); + + floating_rhs_result_in_eax_bool = 0; + + } + +} + +static void emit_load_assignment_rhs_expression_to_reg (const char *reg); +static int rhs_current_operand_is_floating_now (void); + +static int current_argument_is_bare_identifier_now (void) { + + const char *p; + + if (tok.kind != TOK_IDENT || !tok.caret) { + return 0; + } + + p = tok.caret + tok.len; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == ',' || *p == ')'; + +} + +static int emit_push_aggregate_argument_now (const char *name, struct local_symbol *sym) { + + int size; + int offset; + int chunk; + + char memref[64]; + + if (!name || !sym || sym->is_array || sym->pointer_depth > 0 || sym->is_floating) { + return 0; + } + + /** + * Struct/union locals keep their real byte size here. Masking with + * 0x1f is only valid for scalar DATA_* encodings; it turns e.g. a + * 72-byte struct cpu_flags argument into an 8-byte argument. + */ + size = sym->size; + + /* + * Do not treat plain 64-bit scalar locals as aggregate arguments. + * The old test used only size > 4, so a call such as: + * + * bytearray_write_4_bytes (..., result, endianess) + * + * where result is uint_fast64_t/address_type pushed both halves of + * result. The callee expects an unsigned long here, so the extra high + * word shifted the following arguments and pdld wrote broken relocation + * bytes. Real structs/unions either carry an aggregate tag here, or are + * larger than the built-in long long scalar size. + */ + if (size <= (DATA_PTR & 0x1f) || (size <= (DATA_LLONG & 0x1f) && !sym->tag_name)) { + return 0; + } + + if (!state->ofp) { + return 1; + } + + for (offset = size; offset > 0; ) { + + if (offset >= 4) { + + chunk = 4; + offset -= 4; + + } else if (offset >= 2) { + + chunk = 2; + offset -= 2; + + } else { + + chunk = 1; + offset -= 1; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (sym->is_static && sym->static_label) { + + if (chunk == 4) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push dword [%s + %d]\n" : " push dword ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); + } else if (chunk == 2) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word [%s + %d]\n" : " movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " push eax\n"); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte [%s + %d]\n" : " movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " push eax\n"); + } + + } else { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset); + + if (chunk == 4) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push dword %s\n" : " push dword ptr %s\n"), memref); + } else if (chunk == 2) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word %s\n" : " movzx eax, word ptr %s\n"), memref); fprintf (state->ofp, " push eax\n"); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte %s\n" : " movzx eax, byte ptr %s\n"), memref); fprintf (state->ofp, " push eax\n"); + } + + } + + } else { + + if (sym->is_static && sym->static_label) { + + if (chunk == 4) { + fprintf (state->ofp, " pushl %s+%d\n", asm_global_symbol_name (sym->static_label), offset); + } else if (chunk == 2) { + fprintf (state->ofp, " movzwl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushl %%eax\n"); + } else { + fprintf (state->ofp, " movzbl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushl %%eax\n"); + } + + } else { + + if (chunk == 4) { + fprintf (state->ofp, " pushl %ld(%%ebp)\n", sym->offset + offset); + } else if (chunk == 2) { + fprintf (state->ofp, " movzwl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushl %%eax\n"); + } else { + fprintf (state->ofp, " movzbl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushl %%eax\n"); + } + + } + + } + + } + + return 1; + +} + +static int emit_push_global_aggregate_argument_now (const char *name) { + + int size; + int offset; + int chunk; + + if (!name || get_global_symbol_kind (name) != GLOBAL_SYMBOL_OBJECT || + get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0 || + get_global_symbol_floating (name)) { + return 0; + } + + size = get_global_symbol_size (name); + + if (size <= (DATA_PTR & 0x1f)) { + return 0; + } + + if (!state->ofp) { + return 1; + } + + for (offset = size; offset > 0; ) { + + if (offset >= 4) { + + chunk = 4; + offset -= 4; + + } else if (offset >= 2) { + + chunk = 2; + offset -= 2; + + } else { + + chunk = 1; + offset -= 1; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (chunk == 4) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? + " push dword [%s + %d]\n" : + " push dword ptr %s + %d\n"), asm_global_symbol_name (name), offset); + + } else if (chunk == 2) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? + " movzx eax, word [%s + %d]\n" : + " movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (name), offset); + fprintf (state->ofp, " push eax\n"); + + } else { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? + " movzx eax, byte [%s + %d]\n" : + " movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (name), offset); + fprintf (state->ofp, " push eax\n"); + + } + + } else { + + if (chunk == 4) { + fprintf (state->ofp, " pushl %s+%d\n", asm_global_symbol_name (name), offset); + } else if (chunk == 2) { + + fprintf (state->ofp, " movzwl %s+%d, %%eax\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " pushl %%eax\n"); + + } else { + + fprintf (state->ofp, " movzbl %s+%d, %%eax\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " pushl %%eax\n"); + + } + + } + + } + + return 1; + +} + +static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg) { + + int argc = 0; + int total_arg_bytes = 0; + int arg_bytes; + int arg_is_floating; + int i; + int ch; + + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps = 0; + FILE *arg_saved_ofp = 0; + FILE *arg_tmp_ofp = 0; + + if (tok.kind != TOK_LPAREN) { + return; + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " push %s\n", fn_reg); + } else { + fprintf (state->ofp, " pushl %%%s\n", fn_reg); + } + + } + + get_token (); + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + arg_saved_ofp = 0; + arg_tmp_ofp = 0; + + if (state->ofp) { + + arg_tmp_ofp = tmpfile (); + + if (arg_tmp_ofp) { + + arg_saved_ofp = state->ofp; + state->ofp = arg_tmp_ofp; + + } + + } + + postfix_member_seen = 0; + postfix_member_size = 0; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = 0; + + if (tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) { + + struct local_symbol *arg_sym = find_local_symbol (tok.ident); + + arg_bytes = arg_sym ? (arg_sym->size & 0x1f) : DATA_PTR; + arg_is_floating = 0; + + get_token (); + + } else { + + arg_is_floating = rhs_current_operand_is_floating_now (); + arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR; + + if (arg_is_floating) { + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + } else { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } + + if (state->ofp) { + + if (!arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) { + + arg_bytes = postfix_member_size; + emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes); + + } else if (!arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) { + + arg_bytes = postfix_member_size; + emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes); + + } else if (arg_is_floating) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 8\n"); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [esp]\n" : " fstp qword ptr [esp]\n")); + + } else { + + fprintf (state->ofp, " subl $8, %%esp\n"); + fprintf (state->ofp, " fstpl (%%esp)\n"); + + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " push eax\n"); + } else { + fprintf (state->ofp, " pushl %%eax\n"); + } + + } + + } + + } + + total_arg_bytes += arg_bytes; + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + + state->ofp = arg_saved_ofp; + new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1)); + + if (new_arg_tmp_ofps) { + + arg_tmp_ofps = new_arg_tmp_ofps; + arg_tmp_ofps[argc] = arg_tmp_ofp; + arg_tmp_ofp = 0; + + } + + } + + if (arg_tmp_ofp) { + + fclose (arg_tmp_ofp); + arg_tmp_ofp = 0; + + } + + argc++; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + if (state->ofp) { + + for (i = argc - 1; i >= 0; i--) { + + if (arg_tmp_ofps && arg_tmp_ofps[i]) { + + fseek (arg_tmp_ofps[i], 0, SEEK_SET); + + while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { + fputc (ch, state->ofp); + } + + fclose (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + if (arg_tmp_ofps) { + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov ecx, dword [esp + %d]\n", total_arg_bytes); + } else { + fprintf (state->ofp, " mov ecx, dword ptr [esp + %d]\n", total_arg_bytes); + } + + fprintf (state->ofp, " call ecx\n"); + fprintf (state->ofp, " add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f)); + + if (strcmp (result_reg, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", result_reg); + } + + } else { + + fprintf (state->ofp, " movl %d(%%esp), %%ecx\n", total_arg_bytes); + fprintf (state->ofp, " call *%%ecx\n"); + fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f)); + + if (strcmp (result_reg, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", result_reg); + } + + } + + } + + if (arg_tmp_ofps) { + + for (i = 0; i < argc; i++) { + + if (arg_tmp_ofps[i]) { + fclose (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + + } + +} + +static void emit_sub_esp_now (int bytes) { + + if (!state->ofp || bytes <= 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " sub esp, %d\n", bytes); + } else { + fprintf (state->ofp, " subl $%d, %%esp\n", bytes); + } + +} + +static void emit_push_pending_struct_return_address_now (int stack_arg_bytes) { + + if (!state->ofp || (!pending_struct_return_lhs && !pending_struct_return_global_name && !pending_struct_return_stack_address && !pending_struct_return_stack_top)) { + return; + } + + if (pending_struct_return_stack_top) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " lea eax, [esp + %d]\n", stack_arg_bytes); + } else { + fprintf (state->ofp, " lea eax, dword ptr [esp + %d]\n", stack_arg_bytes); + } + + } else { + fprintf (state->ofp, " leal %d(%%esp), %%eax\n", stack_arg_bytes); + } + + } else if (pending_struct_return_stack_address) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov eax, dword [esp + %d]\n", stack_arg_bytes); + } else { + fprintf (state->ofp, " mov eax, dword ptr [esp + %d]\n", stack_arg_bytes); + } + + if (pending_struct_return_stack_offset) { + fprintf (state->ofp, " add eax, %d\n", pending_struct_return_stack_offset); + } + + } else { + + fprintf (state->ofp, " movl %d(%%esp), %%eax\n", stack_arg_bytes); + + if (pending_struct_return_stack_offset) { + fprintf (state->ofp, " addl $%d, %%eax\n", pending_struct_return_stack_offset); + } + + } + + } else if (pending_struct_return_lhs) { + + if (pending_struct_return_lhs->is_static && pending_struct_return_lhs->static_label) { + emit_load_address_to_reg_now ("eax", pending_struct_return_lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("eax", pending_struct_return_lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("eax", pending_struct_return_global_name); + } + + emit_push_reg_now ("eax"); + +} + +static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line) { + + int argc = 0; + int inline_index; + int use_inline = 0; + int expected_inline_args = 0; + int inline_arg_bytes = 0; + int total_arg_bytes = 0; + int arg_bytes; + int arg_is_floating; + int i; + int ch; + + struct local_symbol *saved_pending_struct_return_lhs = pending_struct_return_lhs; + struct local_symbol *call_sym = 0; + + const char *saved_pending_struct_return_global_name = pending_struct_return_global_name; + const char *asm_name; + + int saved_pending_struct_return_stack_address = pending_struct_return_stack_address; + int saved_pending_struct_return_stack_offset = pending_struct_return_stack_offset; + int saved_pending_struct_return_stack_top = pending_struct_return_stack_top; + + FILE *inline_saved_ofp = 0; + FILE *inline_tmp_ofp = 0; + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps = 0; + FILE *arg_saved_ofp = 0; + FILE *arg_tmp_ofp = 0; + + if (tok.kind != TOK_LPAREN) { + return; + } + + inline_index = find_inline_function (name); + + if (inline_index >= 0 && + inline_functions[inline_index].usable && + !inline_functions[inline_index].is_floating && + !inline_functions[inline_index].expanding && + (inline_functions[inline_index].body || inline_functions[inline_index].returns_void) && + (!inline_functions[inline_index].body || inline_body_stack_delta (inline_functions[inline_index].body) == 0)) { + + use_inline = 1; + expected_inline_args = inline_functions[inline_index].param_count; + inline_arg_bytes = expected_inline_args * 4; + + if (state->ofp) { + + inline_tmp_ofp = tmpfile (); + + if (inline_tmp_ofp) { + + inline_saved_ofp = state->ofp; + state->ofp = inline_tmp_ofp; + + } + + if (inline_arg_bytes > 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " sub esp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " subl $%d, %%esp\n", inline_arg_bytes); + } + + } + + } + + } + + get_token (); + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + arg_saved_ofp = 0; + arg_tmp_ofp = 0; + + /* + * cdecl wants the right-most argument nearest the call site and + * the left-most argument at [ebp + 8] in the callee. The old + * code emitted each push immediately while parsing left-to-right, + * which reversed the parameter slots for normal calls. Capture + * each non-inline argument's evaluation/push code and replay the + * completed argument blocks right-to-left just before CALL. + * + * Inline calls keep using the temporary argument frame below: that + * frame intentionally stores argument 0 at [esp], argument 1 at + * [esp + 4], etc., so do not reverse inline argument copies here. + */ + if (!use_inline && state->ofp) { + + arg_tmp_ofp = tmpfile (); + + if (arg_tmp_ofp) { + arg_saved_ofp = state->ofp; + state->ofp = arg_tmp_ofp; + } + + } + + postfix_member_seen = 0; + postfix_member_size = 0; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = 0; + + if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) { + + struct local_symbol *arg_sym = find_local_symbol (tok.ident); + + arg_bytes = arg_sym ? arg_sym->size : DATA_PTR; + arg_is_floating = 0; + + get_token (); + + } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && emit_push_global_aggregate_argument_now (tok.ident)) { + + arg_bytes = get_global_symbol_size (tok.ident); + arg_is_floating = 0; + + get_token (); + + } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now () && + get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION && + get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) { + + char *arg_call_name = xstrdup (tok.ident); + + const char *arg_call_start = tok.start; + const char *arg_call_caret = tok.caret; + + unsigned long arg_call_line = get_line_number (); + + arg_bytes = get_global_symbol_size (arg_call_name); + arg_is_floating = 0; + + emit_sub_esp_now (arg_bytes); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + pending_struct_return_stack_top = 1; + + get_token (); + emit_call_identifier_to_reg_now (arg_call_name, "eax", arg_call_start, arg_call_caret, arg_call_line); + + pending_struct_return_stack_top = 0; + free (arg_call_name); + + } else { + + arg_is_floating = rhs_current_operand_is_floating_now (); + arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR; + + if (arg_is_floating) { + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + } else { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } + + if (state->ofp) { + + if (!use_inline && !arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) { + + arg_bytes = postfix_member_size; + emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes); + + } else if (!use_inline && !arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) { + + arg_bytes = postfix_member_size; + emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes); + + } else if (use_inline) { + + if (argc < expected_inline_args) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp + %d], eax\n" : " mov dword ptr [esp + %d], eax\n"), argc * 4); + } else { + fprintf (state->ofp, " movl %%eax, %d(%%esp)\n", argc * 4); + } + + } + + } else if (arg_is_floating) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 8\n"); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [esp]\n" : " fstp qword ptr [esp]\n")); + + } else { + + fprintf (state->ofp, " subl $8, %%esp\n"); + fprintf (state->ofp, " fstpl (%%esp)\n"); + + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " push eax\n"); + } else { + fprintf (state->ofp, " pushl %%eax\n"); + } + + } + + } + + } + + if (!use_inline) { + total_arg_bytes += arg_bytes; + } + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + state->ofp = arg_saved_ofp; + + new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1)); + + if (new_arg_tmp_ofps) { + + arg_tmp_ofps = new_arg_tmp_ofps; + arg_tmp_ofps[argc] = arg_tmp_ofp; + arg_tmp_ofp = 0; + + } + + } + + if (arg_tmp_ofp) { + + fclose (arg_tmp_ofp); + arg_tmp_ofp = 0; + + } + + argc++; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_has_prototype (name) && ((get_global_symbol_is_variadic (name) && argc < get_global_symbol_param_count (name)) || (!get_global_symbol_is_variadic (name) && argc != get_global_symbol_param_count (name)))) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "wrong number of arguments to function '%s'", name); + } + + if (use_inline) { + + if (argc == expected_inline_args && emit_inline_call_if_possible (name, argc, reg)) { + + if (state->ofp && inline_arg_bytes > 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add esp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " addl $%d, %%esp\n", inline_arg_bytes); + } + + } + + /* + * The peephole inline optimiser currently understands the + * single-argument case well, but its stack-slot liveness pass is + * too aggressive for multi-argument inline calls. It can fold + * constants correctly in simple examples, but it may also remove + * the temporary argument frame and leave confusing label-only + * fragments. Keep multi-argument inline expansion conservative: + * emit the substituted inline body exactly as generated, with the + * argument copies still present. + */ + finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, expected_inline_args <= 1); + return; + + } + + if (state->ofp && inline_arg_bytes > 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add esp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " addl $%d, %%esp\n", inline_arg_bytes); + } + + } + + finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, 0); + return; + + } + + if (emit_inline_call_if_possible (name, argc, reg)) { + + if (arg_tmp_ofps) { + + for (i = 0; i < argc; i++) { + + if (arg_tmp_ofps[i]) { + fclose (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + + } + + return; + } + + call_sym = find_local_symbol (name); + + if (!call_sym) { + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) { + emit_extern_reference_symbol (name, DATA_PTR); + } else { + emit_extern_symbol (name, DATA_PTR, 1); + } + + asm_name = asm_global_symbol_name (name); + + } else { + asm_name = 0; + } + + if (state->ofp) { + + for (i = argc - 1; i >= 0; i--) { + + if (arg_tmp_ofps && arg_tmp_ofps[i]) { + + fseek (arg_tmp_ofps[i], 0, SEEK_SET); + + while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { + fputc (ch, state->ofp); + } + + fclose (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + if (arg_tmp_ofps) { + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + if (saved_pending_struct_return_lhs || saved_pending_struct_return_global_name || + saved_pending_struct_return_stack_address || saved_pending_struct_return_stack_top) { + + pending_struct_return_lhs = saved_pending_struct_return_lhs; + pending_struct_return_global_name = saved_pending_struct_return_global_name; + pending_struct_return_stack_address = saved_pending_struct_return_stack_address; + pending_struct_return_stack_offset = saved_pending_struct_return_stack_offset; + pending_struct_return_stack_top = saved_pending_struct_return_stack_top; + + emit_push_pending_struct_return_address_now (total_arg_bytes); + total_arg_bytes += DATA_PTR & 0x1f; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (call_sym) { + + emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR); + fprintf (state->ofp, " call ecx\n"); + + } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) { + + emit_load_global_to_reg ("ecx", name, DATA_PTR); + fprintf (state->ofp, " call ecx\n"); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (total_arg_bytes > 0) { + fprintf (state->ofp, " add esp, %d\n", total_arg_bytes); + } + + if (strcmp (reg, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } + + } else { + + if (call_sym) { + + emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR); + fprintf (state->ofp, " call *%%ecx\n"); + + } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) { + + emit_load_global_to_reg ("ecx", name, DATA_PTR); + fprintf (state->ofp, " call *%%ecx\n"); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (total_arg_bytes > 0) { + fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes); + } + + if (strcmp (reg, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + } + + set_rhs_last_pointer_info (get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name)); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + pending_struct_return_stack_top = 0; + + if (arg_tmp_ofps) { + + for (i = 0; i < argc; i++) { + + if (arg_tmp_ofps[i]) { + fclose (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + + } + +} + +static void emit_statement_label (int label); +static void emit_statement_jump (int label); + +#define MAX_GOTO_LABELS 256 +#define MAX_GOTO_REFS 512 + +struct goto_label_entry { + + char *name; + + int label; + int defined; + int referenced; + + long defined_stack_size; + unsigned long line; + + const char *start; + const char *caret; + +}; + +static struct goto_label_entry goto_labels[MAX_GOTO_LABELS]; +static int goto_label_count = 0; + +struct goto_ref_entry { + + int label_index; + int ref_label; + + long stack_size; + +}; + +static struct goto_ref_entry goto_refs[MAX_GOTO_REFS]; +static int goto_ref_count = 0; + +static int current_break_label = -1; +static int current_continue_label = -1; + +static long current_break_cleanup_base = 0; +static long current_continue_cleanup_base = 0; + +#define MAX_SWITCH_CASES 256 + +struct switch_case_entry { + + long value; + int label; + +}; + +struct switch_context { + + struct switch_case_entry cases[MAX_SWITCH_CASES]; + int case_count; + int default_label; + int break_label; + +}; + +static struct switch_context *current_switch_context = 0; +static int statement_ends_control_flow; + +static void reset_goto_labels (void) { + + int i; + + for (i = 0; i < goto_label_count; i++) { + + if (goto_labels[i].name) { + free (goto_labels[i].name); + } + + goto_labels[i].name = 0; + goto_labels[i].label = 0; + goto_labels[i].defined = 0; + goto_labels[i].referenced = 0; + goto_labels[i].defined_stack_size = 0; + goto_labels[i].line = 0; + goto_labels[i].start = 0; + goto_labels[i].caret = 0; + + } + + goto_label_count = 0; + goto_ref_count = 0; + +} + +static int find_goto_label (const char *name) { + + int i; + + for (i = 0; i < goto_label_count; i++) { + + if (goto_labels[i].name && strcmp (goto_labels[i].name, name) == 0) { + return i; + } + + } + + return -1; + +} + +static int get_goto_label (const char *name, unsigned long line, const char *start, const char *caret) { + + int i = find_goto_label (name); + + if (i >= 0) { + return i; + } + + if (goto_label_count >= MAX_GOTO_LABELS) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many goto labels"); + return -1; + + } + + i = goto_label_count++; + + goto_labels[i].name = xstrdup (name); + goto_labels[i].label = anon_label++; + goto_labels[i].defined = 0; + goto_labels[i].referenced = 0; + goto_labels[i].defined_stack_size = 0; + goto_labels[i].line = line; + goto_labels[i].start = start; + goto_labels[i].caret = caret; + + return i; + +} + +static void define_goto_label (const char *name, unsigned long line, const char *start, const char *caret) { + + int i = get_goto_label (name, line, start, caret); + + if (i < 0) { + return; + } + + if (goto_labels[i].defined) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate label '%s'", name); + return; + + } + + goto_labels[i].defined = 1; + goto_labels[i].defined_stack_size = current_local_stack_size; + + /** + * A C label marks the next statement. If the previous statement was a + * return, the compiler may have deferred the jump to the common return + * label so it can avoid redundant jumps. Flush it before emitting the + * user label, otherwise the user label will point at the deferred return + * jump instead of the labelled statement. + */ + if (pending_return_jump) { + emit_pending_return_jump (); + } + + emit_statement_label (goto_labels[i].label); + +} + +static void reference_goto_label (const char *name, unsigned long line, const char *start, const char *caret) { + + int i = get_goto_label (name, line, start, caret); + + if (i < 0) { + return; + } + + goto_labels[i].referenced = 1; + + /* + * If this is a forward goto, do not jump directly to the final C + * label. The target may be after automatic declarations in an inner + * block. Because SCC emits stack allocation when those declarations are + * parsed, a direct branch can bypass the allocation and then use invalid + * EBP-relative locals. Emit a per-reference trampoline after the function + * epilogue once the target stack depth is known. + */ + if (!goto_labels[i].defined) { + + if (goto_ref_count < MAX_GOTO_REFS) { + + int ref_label = anon_label++; + + goto_refs[goto_ref_count].label_index = i; + goto_refs[goto_ref_count].ref_label = ref_label; + goto_refs[goto_ref_count].stack_size = current_local_stack_size; + goto_ref_count++; + + emit_statement_jump (ref_label); + return; + + } + + } + + emit_statement_jump (goto_labels[i].label); + +} + +static void check_goto_labels (void) { + + int i; + + for (i = 0; i < goto_label_count; i++) { + + if (goto_labels[i].referenced && !goto_labels[i].defined) { + report_line_at (get_filename (), goto_labels[i].line, REPORT_ERROR, goto_labels[i].start, goto_labels[i].caret, "undefined label '%s'", goto_labels[i].name); + } + + } + +} + +static void emit_goto_trampolines (void) { + + int i; + + if (!state->ofp) { + return; + } + + for (i = 0; i < goto_ref_count; i++) { + + int label_index = goto_refs[i].label_index; + long delta; + + if (label_index < 0 || label_index >= goto_label_count) { + continue; + } + + if (!goto_labels[label_index].defined) { + continue; + } + + emit_statement_label_raw (goto_refs[i].ref_label); + delta = goto_labels[label_index].defined_stack_size - goto_refs[i].stack_size; + + if (delta > 0) { + emit_stack_adjust (delta, 1); + } else if (delta < 0) { + emit_stack_adjust (-delta, 0); + } + + emit_statement_jump (goto_labels[label_index].label); + + } + +} + +static void queue_pending_statement_label (int label) { + + if (label < 0) { + return; + } + + if (pending_statement_label_count >= MAX_PENDING_STATEMENT_LABELS) { + flush_pending_statement_labels (); + } + + if (pending_statement_label_count < MAX_PENDING_STATEMENT_LABELS) { + pending_statement_labels[pending_statement_label_count++] = label; + } else { + emit_statement_label (label); + } + +} + +static void add_switch_case_label (long value, unsigned long line, const char *start, const char *caret) { + + int i; + + if (!current_switch_context) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "case label not within a switch statement"); + return; + + } + + for (i = 0; i < current_switch_context->case_count; i++) { + + if (current_switch_context->cases[i].value == value) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate case value"); + return; + + } + + } + + if (current_switch_context->case_count >= MAX_SWITCH_CASES) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many case labels in switch statement"); + return; + + } + + current_switch_context->cases[current_switch_context->case_count].value = value; + current_switch_context->cases[current_switch_context->case_count].label = anon_label++; + + queue_pending_statement_label (current_switch_context->cases[current_switch_context->case_count].label); + current_switch_context->case_count++; + +} + +static void set_switch_default_label (unsigned long line, const char *start, const char *caret) { + + if (!current_switch_context) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "default label not within a switch statement"); + return; + + } + + if (current_switch_context->default_label >= 0) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate default label"); + return; + + } + + current_switch_context->default_label = anon_label++; + queue_pending_statement_label (current_switch_context->default_label); + +} + +static void emit_switch_dispatch (struct switch_context *sw) { + + int i; + int target; + + if (!state->ofp || !sw) { + return; + } + + for (i = 0; i < sw->case_count; i++) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " cmp eax, %ld\n", sw->cases[i].value); + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, " je L%d\n", sw->cases[i].label); + } else { + fprintf (state->ofp, " je .L%d\n", sw->cases[i].label); + } + + } else { + + fprintf (state->ofp, " cmpl $%ld, %%eax\n", sw->cases[i].value); + fprintf (state->ofp, " je .L%d\n", sw->cases[i].label); + + } + + } + + target = (sw->default_label >= 0) ? sw->default_label : sw->break_label; + emit_statement_jump (target); + +} + +static void parse_switch_statement (void) { + + struct switch_context sw; + struct switch_context *old_switch_context; + + int old_break_label; + int old_continue_label; + + long old_break_cleanup_base; + long old_continue_cleanup_base; + + int saved_ends_control_flow; + + FILE *saved_ofp; + FILE *body_tmp = 0; + + char *body_text = 0; + int body_pending_return_jump = 0; + + sw.case_count = 0; + sw.default_label = -1; + sw.break_label = anon_label++; + + get_token (); + expect (TOK_LPAREN, "("); + + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (tok.kind != TOK_RPAREN) { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + expect (TOK_RPAREN, ")"); + + old_switch_context = current_switch_context; + old_break_label = current_break_label; + old_continue_label = current_continue_label; + old_break_cleanup_base = current_break_cleanup_base; + old_continue_cleanup_base = current_continue_cleanup_base; + + current_switch_context = &sw; + current_break_label = sw.break_label; + current_break_cleanup_base = current_block_cleanup_bytes; + + saved_ofp = state->ofp; + + if (saved_ofp) { + + body_tmp = tmpfile (); + + if (body_tmp) { + state->ofp = body_tmp; + } + + } + + parse_statement (); + saved_ends_control_flow = statement_ends_control_flow; + + if (body_tmp) { + + body_text = read_tmp_file_text (body_tmp); + + fclose (body_tmp); + body_tmp = 0; + + state->ofp = saved_ofp; + + body_pending_return_jump = pending_return_jump; + pending_return_jump = 0; + + emit_switch_dispatch (&sw); + + if (body_text) { + + fputs (body_text, state->ofp); + free (body_text); + + } + + pending_return_jump = body_pending_return_jump; + + } else { + state->ofp = saved_ofp; + } + + emit_statement_label (sw.break_label); + + current_switch_context = old_switch_context; + current_break_label = old_break_label; + current_continue_label = old_continue_label; + current_break_cleanup_base = old_break_cleanup_base; + current_continue_cleanup_base = old_continue_cleanup_base; + + /* + * A break inside the switch only leaves the switch. Do not propagate the + * break statement's statement_ends_control_flow flag to the enclosing + * statement, otherwise code like: + * + * if (x) { switch (y) { case 1: break; } } else { ... } + * + * is compiled without the jump over the else block and the true branch + * falls through into the else body. + */ + (void)saved_ends_control_flow; + statement_ends_control_flow = 0; + +} + +static int is_value_compare_operator (enum token_kind k) { + + return k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER || + k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ; + +} + +static const char *value_compare_set_mnemonic (enum token_kind op, int is_unsigned) { + + switch (op) { + + case TOK_LESS: + + return is_unsigned ? "setb" : "setl"; + + case TOK_LTEQ: + + return is_unsigned ? "setbe" : "setle"; + + case TOK_GREATER: + + return is_unsigned ? "seta" : "setg"; + + case TOK_GTEQ: + + return is_unsigned ? "setae" : "setge"; + + case TOK_EQEQ: + + return "sete"; + + case TOK_NOTEQ: + + return "setne"; + + default: + + return "setne"; + + } + +} + +static void emit_compare_eax_edx_to_reg (enum token_kind op, const char *reg, int is_unsigned) { + + const char *setcc; + + if (!state->ofp) { + return; + } + + setcc = value_compare_set_mnemonic (op, is_unsigned); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " cmp eax, edx\n"); + fprintf (state->ofp, " %s al\n", setcc); + fprintf (state->ofp, " movzx eax, al\n"); + + if (strcmp (reg, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } + + } else { + + fprintf (state->ofp, " cmpl %%edx, %%eax\n"); + fprintf (state->ofp, " %s %%al\n", setcc); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (reg, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + +} + +static int rhs_current_operand_is_unsigned_now (void); + +static int source_lhs_has_char_pointer_cast_before_now (const char *p) { + + const char *q; + int limit = 160; + + if (!p) { + return 0; + } + + q = p; + + while (limit-- > 0 && q > tok.start - 4096) { + + q--; + + if (*q != '(') { + continue; + } + + if ((strncmp (q, "(char", 5) == 0 || strncmp (q, "(unsigned char", 14) == 0 || strncmp (q, "(signed char", 12) == 0) + && strchr (q, '*') && strchr (q, ')') && strchr (q, '*') < p && strchr (q, ')') < p) { + return 1; + } + + if (*q == ';' || *q == ',' || *q == '\n') { + break; + } + + } + + return 0; + +} + +static int arithmetic_operator_precedence_now (enum token_kind op) { + + if (op == TOK_PIPE) { + return 1; + } + + if (op == TOK_CARET) { + return 2; + } + + if (op == TOK_AMPER) { + return 3; + } + + if (op == TOK_LSH || op == TOK_RSH) { + return 4; + } + + if (op == TOK_PLUS || op == TOK_MINUS) { + return 5; + } + + if (op == TOK_STAR || op == TOK_BSLASH || op == TOK_MOD) { + return 6; + } + + return 0; + +} + +static int emit_load_assignment_binary_expression_prec_to_reg (const char *reg, int min_prec) { + + int is_unsigned; + int expr_pointer_depth; + int expr_pointed_size; + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg (reg); + + /* + * Some statement-condition paths can leave a postfix member chain after + * the primary operand, e.g. inside parenthesized logical RHS terms such as + * && (reg->type.dword || reg->type.debug) + * Consume that postfix here before the binary/logical expression parser + * decides whether the operand is complete. Otherwise the enclosing + * parenthesized-expression code sees the still-pending "->"/"." token and + * reports a false "expected )". + */ + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now (reg); + } + + /* + * The postfix member helper deliberately leaves aggregates and 64-bit + * scalar members as an address because the pair/aggregate paths may need + * the full object. This single-register expression path, however, only + * has EAX/EDX-style 32-bit arithmetic available. When such a member is + * used inside ordinary arithmetic/comparison code (for example the + * CHECK_READ macro in pdld with address_type fields), load the low word + * from the member address instead of adding the member address itself. + */ + if (postfix_member_seen + && postfix_member_pointer_depth == 0 + && postfix_member_size == (DATA_LLONG & 0x1f) + && !postfix_member_is_floating) { + + emit_load_deref_reg_now (reg, DATA_INT & 0x1f); + postfix_member_size = DATA_INT & 0x1f; + + } + + if (postfix_member_seen && postfix_member_is_unsigned) { + is_unsigned = 1; + } + + expr_pointer_depth = rhs_last_pointer_depth; + expr_pointed_size = rhs_last_pointed_size; + + while (is_arithmetic_binary_operator (tok.kind) && arithmetic_operator_precedence_now (tok.kind) >= min_prec) { + + enum token_kind op; + + int prec; + int rhs_is_unsigned; + int lhs_pointer_depth; + int lhs_pointed_size; + int rhs_pointer_depth; + int rhs_pointed_size; + int scale_rhs = 0; + int scale_lhs = 0; + + op = tok.kind; + prec = arithmetic_operator_precedence_now (op); + + lhs_pointer_depth = expr_pointer_depth; + lhs_pointed_size = expr_pointed_size; + + if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 && + source_lhs_has_char_pointer_cast_before_now (tok.caret)) { + lhs_pointed_size = DATA_CHAR & 0x1f; + } + + get_token (); + rhs_is_unsigned = rhs_current_operand_is_unsigned_now (); + + if (rhs_is_unsigned) { + is_unsigned = 1; + } + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%eax\n", reg); + } + + } + + emit_push_reg_now ("eax"); + rhs_is_unsigned = emit_load_assignment_binary_expression_prec_to_reg ("edx", prec + 1); + + if (rhs_is_unsigned) { + is_unsigned = 1; + } + + rhs_pointer_depth = rhs_last_pointer_depth; + rhs_pointed_size = rhs_last_pointed_size; + + if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 && rhs_pointer_depth == 0) { + scale_rhs = index_step_size (lhs_pointed_size); + } else if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) { + scale_lhs = index_step_size (rhs_pointed_size); + } + + if (scale_rhs > 1) { + emit_scale_reg_by_const_now ("edx", scale_rhs); + } + + emit_pop_reg_now ("eax"); + + if (scale_lhs > 1) { + emit_scale_reg_by_const_now ("eax", scale_lhs); + } + + emit_assignment_binary_op (op, is_unsigned); + + if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0 && index_step_size (lhs_pointed_size) > 1) { + emit_divide_eax_by_const_now (index_step_size (lhs_pointed_size)); + } + + if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) { + + expr_pointer_depth = rhs_pointer_depth; + expr_pointed_size = rhs_pointed_size; + + } else if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0) { + + expr_pointer_depth = 0; + expr_pointed_size = 0; + + } else if (op != TOK_PLUS && op != TOK_MINUS) { + + expr_pointer_depth = 0; + expr_pointed_size = 0; + + } + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } else { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + } + + set_rhs_last_pointer_info (expr_pointer_depth, expr_pointed_size); + return is_unsigned; + +} + +static void emit_load_assignment_binary_expression_to_reg (const char *reg) { + + if (const_integer_expr_text_is_foldable_now (tok.caret)) { + + int64_s v = const64_from_current_foldable_expr (); + emit_load_const32_to_reg_now (reg, v); + + return; + + } + + emit_load_assignment_binary_expression_prec_to_reg (reg, 1); + +} + +static void emit_load_assignment_compare_expression_to_reg (const char *reg) { + + enum token_kind op; + + int lhs_pointer_depth; + int is_unsigned; + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + if (emit_load_assignment_binary_expression_prec_to_reg (reg, 1)) { + is_unsigned = 1; + } + + lhs_pointer_depth = rhs_last_pointer_depth; + + if (is_value_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%eax\n", reg); + } + + } + + emit_push_reg_now ("eax"); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_binary_expression_to_reg ("edx"); + + if (lhs_pointer_depth > 0 || rhs_last_pointer_depth > 0) { + is_unsigned = 1; + } + + emit_pop_reg_now ("eax"); + emit_compare_eax_edx_to_reg (op, reg, is_unsigned); + + } + +} + +static void emit_test_reg_jump_zero_now (const char *reg, int label) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test %s, %s\n", reg, reg); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label); + + } else { + + fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg); + fprintf (state->ofp, " jz .L%d\n", label); + + } + +} + +static void emit_test_reg_jump_nonzero_now (const char *reg, int label) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test %s, %s\n", reg, reg); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), label); + + } else { + + fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg); + fprintf (state->ofp, " jnz .L%d\n", label); + + } + +} + +static void emit_mov_imm_to_reg_now (const char *reg, long value) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %ld\n", reg, value); + } else { + fprintf (state->ofp, " movl $%ld, %%%s\n", value, reg); + } + +} + +static void emit_floating_stack_to_int_reg_now (const char *reg) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp dword [esp]\n" : " fistp dword ptr [esp]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", reg); + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " fistpl (%%esp)\n"); + fprintf (state->ofp, " movl (%%esp), %%%s\n", reg); + fprintf (state->ofp, " addl $4, %%esp\n"); + + } + +} + +static void emit_load_assignment_rhs_expression_to_reg (const char *reg) { + + int false_label; + int true_label; + int end_label; + + enum token_kind logop; + + if (rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + emit_floating_stack_to_int_reg_now (reg); + + return; + + } + + emit_load_assignment_compare_expression_to_reg (reg); + + while (tok.kind == TOK_LOGAND || tok.kind == TOK_LOGOR) { + + logop = tok.kind; + false_label = anon_label++; + true_label = anon_label++; + end_label = anon_label++; + + get_token (); + + if (logop == TOK_LOGAND) { + + emit_test_reg_jump_zero_now (reg, false_label); + emit_load_assignment_rhs_expression_to_reg (reg); + emit_test_reg_jump_zero_now (reg, false_label); + emit_mov_imm_to_reg_now (reg, 1); + emit_statement_jump (end_label); + emit_statement_label (false_label); + emit_mov_imm_to_reg_now (reg, 0); + emit_statement_label (end_label); + + } else { + + emit_test_reg_jump_nonzero_now (reg, true_label); + emit_load_assignment_rhs_expression_to_reg (reg); + emit_test_reg_jump_nonzero_now (reg, true_label); + emit_mov_imm_to_reg_now (reg, 0); + emit_statement_jump (end_label); + emit_statement_label (true_label); + emit_mov_imm_to_reg_now (reg, 1); + emit_statement_label (end_label); + + } + + } + + if (tok.kind == TOK_QMARK) { + + false_label = anon_label++; + end_label = anon_label++; + + get_token (); + + if (state->ofp) { + + if (strcmp (reg, "eax") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%eax\n", reg); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test eax, eax\n"); + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, " jz L%d\n", false_label); + } else { + fprintf (state->ofp, " jz .L%d\n", false_label); + } + + } else { + + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, " jz .L%d\n", false_label); + + } + + } + + emit_load_assignment_rhs_expression_to_reg (reg); + expect (TOK_COLON, ":"); + emit_statement_jump (end_label); + emit_statement_label (false_label); + emit_load_assignment_rhs_expression_to_reg (reg); + emit_statement_label (end_label); + + } + +} + +static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned); + +static int is_assignment64_binary_operator (enum token_kind k) { + + return is_arithmetic_binary_operator (k) || + k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER || + k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ || + k == TOK_LOGAND || k == TOK_LOGOR; + +} + +static void emit_assignment64_bool_result_to_pair_now (const char *lo, const char *hi) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " mov %s, eax\n", lo); + } + + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } else { + + if (strcmp (lo, "eax") != 0) { + fprintf (state->ofp, " movl %%eax, %%%s\n", lo); + } + + fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi); + + } + +} + +static void emit_assignment64_logical_op_to_pair_now (enum token_kind op, const char *lo, const char *hi) { + + int true_label; + int false_label; + int end_label; + + if (!state->ofp) { + return; + } + + true_label = anon_label++; + false_label = anon_label++; + end_label = anon_label++; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test edx, edx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label); + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label); + + if (op == TOK_LOGAND) { + + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), false_label); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label); + fprintf (state->ofp, " test ecx, ecx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label); + fprintf (state->ofp, " test ebx, ebx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), end_label + 1); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label); + fprintf (state->ofp, " mov eax, 1\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label + 1); + + } else { + + fprintf (state->ofp, " test ecx, ecx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label); + fprintf (state->ofp, " test ebx, ebx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), end_label); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label); + fprintf (state->ofp, " mov eax, 1\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label); + + } + + } else { + + fprintf (state->ofp, " testl %%edx, %%edx\n"); + fprintf (state->ofp, op == TOK_LOGOR ? " jnz .L%d\n" : " jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label); + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, op == TOK_LOGOR ? " jnz .L%d\n" : " jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label); + + if (op == TOK_LOGAND) { + + fprintf (state->ofp, " jmp .L%d\n", false_label); + fprintf (state->ofp, ".L%d:\n", end_label); + fprintf (state->ofp, " testl %%ecx, %%ecx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testl %%ebx, %%ebx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " jmp .L%d\n", end_label + 1); + fprintf (state->ofp, ".L%d:\n", true_label); + fprintf (state->ofp, " movl $1, %%eax\n"); + fprintf (state->ofp, ".L%d:\n", end_label + 1); + + } else { + + fprintf (state->ofp, " testl %%ecx, %%ecx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testl %%ebx, %%ebx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " jmp .L%d\n", end_label); + fprintf (state->ofp, ".L%d:\n", true_label); + fprintf (state->ofp, " movl $1, %%eax\n"); + fprintf (state->ofp, ".L%d:\n", end_label); + + } + + } + + anon_label++; + emit_assignment64_bool_result_to_pair_now (lo, hi); + +} + +static void emit_assignment64_compare_op_to_pair_now (enum token_kind op, const char *lo, const char *hi, int is_unsigned) { + + emit_statement_cmp64_to_eax (op, is_unsigned); + emit_assignment64_bool_result_to_pair_now (lo, hi); + +} + +static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned) { + + enum token_kind op; + + int result_pair_is_eax_edx = (strcmp (lo, "eax") == 0 && strcmp (hi, "edx") == 0); + int current_pair_is_eax_edx = result_pair_is_eax_edx; + + if (const_integer_expr_text_is_foldable_now (tok.start)) { + + int64_s v = const64_from_current_foldable_expr (); + emit_load_const64_to_pair_now (lo, hi, v); + + return; + + } + + emit_load_assignment_rhs_to_pair (lo, hi); + + while (is_assignment64_binary_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (!current_pair_is_eax_edx) { + + emit_mov_reg_to_reg_now ("eax", lo); + emit_mov_reg_to_reg_now ("edx", hi); + + current_pair_is_eax_edx = 1; + + } + + emit_preserve_assignment64_regs (op); + + /* + * The right operand of a 64-bit shift is a plain integer shift + * count, not a 64-bit value. Loading it through the 64-bit primary + * path loses precedence for cases such as: + * + * ((address_type)1) << (CHAR_BIT * rel->howto->size) + * + * Worse, the RHS loader uses EAX internally for nested expressions; + * preserve the 64-bit LHS in EDX:EAX while the count is evaluated + * into EBX. + */ + if (op == TOK_LSH || op == TOK_RSH || op == TOK_LSHEQ || op == TOK_RSHEQ) { + + emit_push_reg_now ("eax"); + emit_push_reg_now ("edx"); + + emit_load_assignment_binary_expression_to_reg ("ebx"); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor ecx, ecx\n"); + } else { + fprintf (state->ofp, " xorl %%ecx, %%ecx\n"); + } + + emit_pop_reg_now ("edx"); + emit_pop_reg_now ("eax"); + + } else { + + /* + * The generic 64-bit RHS loader uses EAX:EDX as scratch even + * when asked to leave the final value in EBX:ECX. Preserve the + * left operand around RHS evaluation; otherwise expressions such + * as: + * + * result &= (((address_type)1) << n) - 1 + * + * end up applying the operator to the RHS twice, because the + * computed mask clobbers the original result in EAX:EDX. + */ + emit_push_reg_now ("eax"); + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_to_pair ("ebx", "ecx"); + + emit_pop_reg_now ("edx"); + emit_pop_reg_now ("eax"); + + } + + if (op == TOK_LOGAND || op == TOK_LOGOR) { + + emit_assignment64_logical_op_to_pair_now (op, lo, hi); + current_pair_is_eax_edx = result_pair_is_eax_edx; + + } else if (is_value_compare_operator (op)) { + + emit_assignment64_compare_op_to_pair_now (op, lo, hi, is_unsigned); + current_pair_is_eax_edx = result_pair_is_eax_edx; + + } else { + + emit_assignment_binary_op64 (op, is_unsigned); + current_pair_is_eax_edx = 1; + + } + + emit_restore_assignment64_regs (op); + + } + + if (current_pair_is_eax_edx && !result_pair_is_eax_edx) { + + emit_mov_reg_to_reg_now (lo, "eax"); + emit_mov_reg_to_reg_now (hi, "edx"); + + current_pair_is_eax_edx = 0; + + } + + if (tok.kind == TOK_QMARK) { + + int false_label = anon_label++; + int end_label = anon_label++; + + get_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test %s, %s\n", hi, hi); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), false_label + 2); + fprintf (state->ofp, " test %s, %s\n", lo, lo); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), false_label); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label + 2); + + } else { + + fprintf (state->ofp, " testl %%%s, %%%s\n", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", false_label + 2); + fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " jz .L%d\n", false_label); + fprintf (state->ofp, ".L%d:\n", false_label + 2); + + } + + } + + anon_label++; + + emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned); + expect (TOK_COLON, ":"); + + emit_statement_jump (end_label); + emit_statement_label (false_label); + emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned); + emit_statement_label (end_label); + + } + +} + +static void emit_incdec_integral_symbol_now (struct local_symbol *sym, const char *name, int size, enum token_kind op) { + + char memref[64]; + char nasm_memref[256]; + + const char *symbol; + const char *mnemonic; + + if (!state->ofp) { + return; + } + + if (size == (DATA_LLONG & 0x1f)) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global64_to_pair ("eax", "edx", sym->static_label); + } else { + emit_load_local64_to_pair (sym->offset, "eax", "edx"); + } + + } else { + emit_load_global64_to_pair ("eax", "edx", name); + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (op == TOK_INCR) { + + fprintf (state->ofp, " add eax, 1\n"); + fprintf (state->ofp, " adc edx, 0\n"); + + } else { + + fprintf (state->ofp, " sub eax, 1\n"); + fprintf (state->ofp, " sbb edx, 0\n"); + + } + + } else { + + if (op == TOK_INCR) { + + fprintf (state->ofp, " addl $1, %%eax\n"); + fprintf (state->ofp, " adcl $0, %%edx\n"); + + } else { + + fprintf (state->ofp, " subl $1, %%eax\n"); + fprintf (state->ofp, " sbbl $0, %%edx\n"); + + } + + } + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_store_pair_to_global64 (sym->static_label, "eax", "edx"); + } else { + emit_store_pair_to_local64 (sym->offset, "eax", "edx"); + } + + } else { + emit_store_pair_to_global64 (name, "eax", "edx"); + } + + return; + + } + + if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f) && size != (DATA_INT & 0x1f) && size != DATA_PTR) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + mnemonic = op == TOK_INCR ? "add" : "sub"; + + if (sym && !sym->is_static) { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + symbol = memref; + + } else if (sym && sym->static_label) { + symbol = asm_global_symbol_name (sym->static_label); + } else { + symbol = asm_global_symbol_name (name); + } + + if (state->syntax & ASM_SYNTAX_NASM) { + symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol); + } + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s byte %s, 1\n" : " %s byte ptr %s, 1\n"), mnemonic, symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s word %s, 1\n" : " %s word ptr %s, 1\n"), mnemonic, symbol); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, 1\n" : " %s dword ptr %s, 1\n"), mnemonic, symbol); + } + + } else { + + if (sym && !sym->is_static) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, op == TOK_INCR ? " incb %ld(%%ebp)\n" : " decb %ld(%%ebp)\n", sym->offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, op == TOK_INCR ? " incw %ld(%%ebp)\n" : " decw %ld(%%ebp)\n", sym->offset); + } else { + fprintf (state->ofp, op == TOK_INCR ? " incl %ld(%%ebp)\n" : " decl %ld(%%ebp)\n", sym->offset); + } + + } else { + + symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, op == TOK_INCR ? " incb %s\n" : " decb %s\n", symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, op == TOK_INCR ? " incw %s\n" : " decw %s\n", symbol); + } else { + fprintf (state->ofp, op == TOK_INCR ? " incl %s\n" : " decl %s\n", symbol); + } + + } + + } + +} + +static void emit_incdec_pointer_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int step) { + + const char *mnemonic = op == TOK_INCR ? "add" : "sub"; + const char *symbol; + + char memref[64]; + char nasm_memref[128]; + + if (step <= 0) { + step = 1; + } + + if (!state->ofp) { + return; + } + + if (step == 1) { + + emit_incdec_integral_symbol_now (sym, name, DATA_PTR, op); + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (sym && !sym->is_static) { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + symbol = memref; + + } else if (sym && sym->static_label) { + symbol = asm_global_symbol_name (sym->static_label); + } else { + symbol = asm_global_symbol_name (name); + } + + if (state->syntax & ASM_SYNTAX_NASM) { + + symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol); + fprintf (state->ofp, " %s dword %s, %d\n", mnemonic, symbol, step); + + } else { + fprintf (state->ofp, " %s dword ptr %s, %d\n", mnemonic, symbol, step); + } + + } else { + + if (sym && !sym->is_static) { + fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", mnemonic, step, sym->offset); + } else { + + symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name); + fprintf (state->ofp, " %sl $%d, %s\n", mnemonic, step, symbol); + + } + + } + +} + +static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret) { + + int global_index; + int is_floating; + int pointer_depth; + int pointed_size; + + int size; + + if (!sym) { + + global_index = find_global_symbol (name); + + if (global_index < 0) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name); + return; + + } + + if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) { + + report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "function '%s' cannot be incremented or decremented", name); + return; + + } + + } + + size = sym ? sym->size : get_global_symbol_size (name); + is_floating = sym ? sym->is_floating : get_global_symbol_floating (name); + + pointer_depth = sym ? sym->pointer_depth : get_global_symbol_pointer_depth (name); + pointed_size = sym ? sym->pointed_size : get_global_symbol_pointed_size (name); + + if (!state->ofp) { + return; + } + + if (is_floating) { + + emit_load_floating_symbol_now (sym, name, size); + fprintf (state->ofp, " fld1\n"); + + if (op == TOK_INCR) { + emit_floating_binary_now (TOK_PLUS); + } else { + emit_floating_binary_now (TOK_MINUS); + } + + emit_store_floating_symbol_now (sym, name, size); + return; + + } + + if (pointer_depth > 0) { + + emit_incdec_pointer_symbol_now (sym, name, op, pointed_size); + return; + + } + + emit_incdec_integral_symbol_now (sym, name, size, op); + +} + +static int parse_prefix_incdec_statement (void) { + + enum token_kind op; + char *name; + + const char *name_start, *name_caret; + unsigned long name_line; + + struct local_symbol *sym; + + int deref_size = DATA_INT & 0x1f; + int indirect = 0; + + if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now ("eax")) { + + expect_semi_or_recover (); + return 1; + + } + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + op = tok.kind; + get_token (); + + if (tok.kind == TOK_STAR) { + + indirect = 1; + get_token (); + + } + + if (tok.kind != TOK_IDENT) { + + if (indirect) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *"); + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--"); + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + sym = find_local_symbol (name); + + if (indirect) { + + if (sym) { + + if (sym->pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) { + deref_size = sym->pointed_size & 0x1f; + } + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", sym->offset, DATA_PTR); + } + + } else if (find_global_symbol (name) >= 0) { + + if (get_global_symbol_pointer_depth (name) > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) { + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + } + + emit_load_global_to_reg ("edx", name, DATA_PTR); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + emit_push_reg_now ("edx"); + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add eax, 1\n" : " sub eax, 1\n"); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addl $1, %%eax\n" : " subl $1, %%eax\n"); + } + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret); + + expect_semi_or_recover (); + free (name); + + return 1; + +} + +static int parse_parenthesized_pointer_member_indirect_assignment_statement (void) { + + char *name = 0; + char *member = 0; + + struct local_symbol *sym; + + int global_index; + int parens = 0; + int offset = 0; + int member_size = DATA_PTR & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int deref_size = DATA_INT & 0x1f; + int step = 1; + + enum token_kind postfix_op = TOK_EOF; + enum token_kind op; + + const char *name_start; + const char *name_caret; + const char *member_start; + const char *member_caret; + + unsigned long name_line; + unsigned long member_line; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + while (tok.kind == TOK_LPAREN) { + ++parens; + get_token (); + } + + if (tok.kind != TOK_STAR) { + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind prefix_op = tok.kind; + char *prefix_name; + + const char *prefix_start; + const char *prefix_caret; + + unsigned long prefix_line; + + struct local_symbol *prefix_sym; + + int prefix_global_index; + int prefix_deref_size = DATA_INT & 0x1f; + + enum token_kind prefix_assign_op; + get_token (); + + if (tok.kind != TOK_IDENT) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + prefix_name = xstrdup (tok.ident); + prefix_start = tok.start; + prefix_caret = tok.caret; + prefix_line = get_line_number (); + + get_token (); + + while (tok.kind == TOK_RPAREN && parens > 0) { + + --parens; + get_token (); + + } + + if (parens != 0 || !is_assignment_operator (tok.kind)) { + + free (prefix_name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + prefix_assign_op = tok.kind; + get_token (); + + prefix_sym = find_local_symbol (prefix_name); + prefix_global_index = find_global_symbol (prefix_name); + + if (!prefix_sym && prefix_global_index < 0) { + + report_line_at (get_filename (), prefix_line, REPORT_ERROR, prefix_start, prefix_caret, "unknown symbol '%s'", prefix_name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (prefix_name); + return 1; + + } + + if (prefix_sym) { + + if (prefix_sym->pointer_depth > 1) { + prefix_deref_size = DATA_PTR & 0x1f; + } else if (prefix_sym->pointer_depth == 1 && prefix_sym->pointed_size > 0) { + prefix_deref_size = prefix_sym->pointed_size & 0x1f; + } + + } else { + + if (get_global_symbol_pointer_depth (prefix_name) > 1) { + prefix_deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (prefix_name) == 1 && get_global_symbol_pointed_size (prefix_name) > 0) { + prefix_deref_size = get_global_symbol_pointed_size (prefix_name) & 0x1f; + } + + } + + if (prefix_deref_size == 0) { + prefix_deref_size = DATA_INT & 0x1f; + } + + emit_incdec_symbol_now (prefix_sym, prefix_name, prefix_op, prefix_line, prefix_start, prefix_caret); + + if (state->ofp) { + + if (prefix_sym) { + + if (prefix_sym->is_static && prefix_sym->static_label) { + emit_load_global_to_reg ("ecx", prefix_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", prefix_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", prefix_name, DATA_PTR); + } + + emit_push_reg_now ("ecx"); + + if (prefix_assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_pop_reg_now ("edx"); + emit_push_reg_now ("edx"); + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, prefix_deref_size); + emit_push_reg_now ("eax"); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + emit_assignment_binary_op (prefix_assign_op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", prefix_deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (prefix_name); + return 1; + + } + + if (tok.kind == TOK_IDENT) { + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_op = tok.kind; + get_token (); + + } + + while (tok.kind == TOK_RPAREN && parens > 0) { + + --parens; + get_token (); + + } + + if (parens != 0 || !is_assignment_operator (tok.kind)) { + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!sym && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (sym) { + + if (sym->pointer_depth > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (sym->pointer_depth == 1) { + + deref_size = sym->pointed_size & 0x1f; + step = sym->pointed_size > 0 ? sym->pointed_size : 1; + + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (get_global_symbol_pointer_depth (name) == 1) { + + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1; + + } + + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("ecx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", name, DATA_PTR); + } + + emit_push_reg_now ("ecx"); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + + if (sym) { + + if (sym->is_static && sym->static_label) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", sym->static_label, step); + } else { + fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->static_label); + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + char memref[64]; + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, %d\n" : " %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step); + + } else { + fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->offset); + } + + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step); + } else { + fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name); + } + + } + + } + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_pop_reg_now ("edx"); + emit_push_reg_now ("edx"); + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + get_token (); + + while (tok.kind == TOK_LPAREN) { + + ++parens; + get_token (); + + } + + if (tok.kind != TOK_IDENT) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + if (state->ofp) { + + if (tok.kind == TOK_LPAREN) { + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + } else { + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("eax", sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg ("eax", name, DATA_PTR); + } else { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + } + + emit_load_deref_reg_now ("eax", DATA_PTR); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, eax\n"); + } else { + fprintf (state->ofp, " movl %%eax, %%edx\n"); + } + + } else if (tok.kind == TOK_LPAREN) { + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + } + + while (tok.kind == TOK_RPAREN && parens > 0) { + + --parens; + get_token (); + + } + + if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_op = tok.kind; + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + if (sym->pointer_depth > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (sym->pointer_depth == 1) { + + deref_size = sym->pointed_size & 0x1f; + step = 1; + + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + + if (get_global_symbol_pointer_depth (name) > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (get_global_symbol_pointer_depth (name) == 1) { + + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + step = 1; + + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + emit_load_global_to_reg ("edx", name, DATA_PTR); + + } else { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + if (state->ofp) { + + emit_load_deref_reg_now ("edx", deref_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s edx, %d\n", postfix_op == TOK_INCR ? "add" : "sub", step); + } else { + fprintf (state->ofp, " %sl $%d, %%edx\n", postfix_op == TOK_INCR ? "add" : "sub", step); + } + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("eax", sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg ("eax", name, DATA_PTR); + } + + emit_store_reg_to_deref_reg_now ("eax", "edx", deref_size); + + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name"); + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + free (member); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_op = tok.kind; + get_token (); + + } + + if (!is_assignment_operator (tok.kind)) { + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + if (member_pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (member_pointer_depth == 1 && member_elem_size > 0) { + deref_size = member_elem_size & 0x1f; + } else if (member_size > 0) { + deref_size = member_size & 0x1f; + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + step = member_elem_size > 0 ? member_elem_size : 1; + + if (state->ofp) { + + emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } else { + fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } + + } else { + fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset); + } + + } + + emit_push_reg_now ("ecx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + +} + +static int paren_text_starts_type_name_now (void) { + + const char *p; + + char name[128]; + int n = 0; + + if (tok.caret) { + p = tok.caret; + } else if (tok.start) { + p = tok.start; + } else { + return 0; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(') { + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && n < (int) sizeof (name) - 1) { + name[n++] = *p++; + } + + name[n] = '\0'; + + if (strcmp (name, "char") == 0 || strcmp (name, "short") == 0 || + strcmp (name, "int") == 0 || strcmp (name, "long") == 0 || + strcmp (name, "signed") == 0 || strcmp (name, "unsigned") == 0 || + strcmp (name, "void") == 0 || strcmp (name, "struct") == 0 || + strcmp (name, "union") == 0 || strcmp (name, "enum") == 0) { + return 1; + } + + return find_typedef_name (name) != 0; + +} + +static int parse_cast_indirect_assignment_statement (void) { + + int saved_type_size = parsed_type_size; + int saved_storage_class = parsed_storage_class; + int saved_is_aggregate = parsed_type_is_aggregate; + int saved_is_void = parsed_type_is_void; + int saved_is_unsigned = parsed_type_is_unsigned; + int saved_is_floating = parsed_type_is_floating; + int saved_has_tag = parsed_type_has_tag; + int saved_is_inline = parsed_type_is_inline; + int saved_field_count = parsed_field_count; + int saved_fields[MAX_AGG_FIELDS]; + int saved_declarator_is_pointer = declarator_is_pointer; + int saved_declarator_pointer_depth = declarator_pointer_depth; + int saved_declarator_has_array = declarator_has_array; + int saved_declarator_has_function = declarator_has_function; + int saved_declarator_array_unsized = declarator_array_unsized; + long saved_declarator_array_count = declarator_array_count; + + char *cast_name = 0; + int base_size; + int deref_size; + int pointer_depth; + int i; + + enum token_kind op; + int has_outer_paren = 0; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + /* + * Accept the common casted-dereference lvalue spelling: + * + * *((char *)ptr + n) = v; + * + * parse_indirect_assignment_statement() enters here at the outer '('; + * the actual cast type begins after the inner '('. + */ + if (tok.kind == TOK_LPAREN) { + + has_outer_paren = 1; + get_token (); + + } + + if (!is_type_start (tok.kind)) { + return 0; + } + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + parse_type_spec (); + base_size = parsed_type_size & 0x1f; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&cast_name); + } + + pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0; + deref_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : base_size; + + if (deref_size <= 0) { + deref_size = DATA_INT & 0x1f; + } + + if (cast_name) { + free (cast_name); + } + + expect (TOK_RPAREN, ")"); + + parsed_type_size = saved_type_size; + parsed_storage_class = saved_storage_class; + parsed_type_is_aggregate = saved_is_aggregate; + parsed_type_is_void = saved_is_void; + parsed_type_is_unsigned = saved_is_unsigned; + parsed_type_is_floating = saved_is_floating; + parsed_type_has_tag = saved_has_tag; + parsed_type_is_inline = saved_is_inline; + + clear_parsed_fields (); + + for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) { + parsed_field_sizes[i] = saved_fields[i]; + } + + parsed_field_count = saved_field_count; + + declarator_is_pointer = saved_declarator_is_pointer; + declarator_pointer_depth = saved_declarator_pointer_depth; + declarator_has_array = saved_declarator_has_array; + declarator_has_function = saved_declarator_has_function; + declarator_array_unsized = saved_declarator_array_unsized; + declarator_array_count = saved_declarator_array_count; + + if (state->ofp) { + + /* + * Parse the address expression inside the outer dereference without + * allowing the first operand parser to consume the assignment operator. + * + * For: + * + * *(size_t *)ptr = size; + * + * emit_load_assignment_rhs_to_reg() sees the identifier "ptr" followed + * by '=' and treats it as an assignment expression, generating + * "ptr = size" instead of using ptr as the destination address. Load a + * plain identifier operand directly, then let the explicit '+'/'-' loop + * below handle the casted pointer arithmetic case: + * + * *((char *)ptr + *actualRead) = '\n'; + */ + if (tok.kind == TOK_IDENT) { + + char *addr_name = xstrdup (tok.ident); + + const char *addr_start = tok.start; + const char *addr_caret = tok.caret; + + unsigned long addr_line = get_line_number (); + + struct local_symbol *addr_sym; + int addr_global_index; + + get_token (); + + addr_sym = find_local_symbol (addr_name); + addr_global_index = find_global_symbol (addr_name); + + if (addr_sym) { + + if (addr_sym->is_static && addr_sym->static_label) { + emit_load_global_to_reg ("edx", addr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", addr_sym->offset, DATA_PTR); + } + + } else if (addr_global_index >= 0) { + emit_load_global_to_reg ("edx", addr_name, DATA_PTR); + } else { + report_line_at (get_filename (), addr_line, REPORT_ERROR, addr_start, addr_caret, "unknown symbol '%s'", addr_name); + } + + free (addr_name); + + } else { + emit_load_assignment_rhs_to_reg ("edx"); + } + + while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + enum token_kind addr_op = tok.kind; + get_token (); + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_to_reg ("eax"); + + if (deref_size > 1) { + emit_scale_reg_by_const_now ("eax", deref_size); + } + + emit_pop_reg_now ("edx"); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s edx, eax\n", addr_op == TOK_PLUS ? "add" : "sub"); + } else { + fprintf (state->ofp, " %sl %%eax, %%edx\n", addr_op == TOK_PLUS ? "add" : "sub"); + } + + } + + } else { + skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF); + } + + if (has_outer_paren) { + expect (TOK_RPAREN, ")"); + } + + if (!is_assignment_operator (tok.kind)) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + op = tok.kind; + get_token (); + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_push_reg_now ("edx"); + emit_load_deref_reg_now ("eax", deref_size); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + expect_semi_or_recover (); + + return 1; + +} + +static int parse_indirect_assignment_statement (void) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *lhs; + + int global_index; + int deref_size = DATA_INT & 0x1f; + + enum token_kind op; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now ("eax")) { + + expect_semi_or_recover (); + return 1; + + } + + get_token (); + + if (tok.kind == TOK_STAR) { + + get_token (); + + if (tok.kind == TOK_IDENT) { + + name = xstrdup (tok.ident); + + name_start = tok.start; + name_caret = tok.caret; + + name_line = get_line_number (); + get_token (); + + if (is_assignment_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (lhs) { + + if (lhs->pointer_depth > 2) { + deref_size = DATA_PTR & 0x1f; + } else if (lhs->pointer_depth == 2) { + deref_size = lhs->pointed_size & 0x1f; + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 2) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (name) == 2) { + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + } + + } + + if (deref_size <= 0) { + deref_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", name, DATA_PTR); + } + + emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f); + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("ecx"); + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("ecx"); + + } else { + + emit_push_reg_now ("ecx"); + + emit_load_member_from_addr_reg_now ("eax", "ecx", 0, deref_size); + emit_push_reg_now ("eax"); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("ecx"); + + } + + emit_store_reg_to_deref_reg_now ("ecx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + free (name); + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + return 1; + + } + + if (tok.kind != TOK_IDENT) { + + if (tok.kind == TOK_LPAREN) { + + if (paren_text_starts_type_name_now () || + (tok.start && tok.start[0] == '(' && tok.start[1] == '(')) { + return parse_cast_indirect_assignment_statement (); + } + + return parse_parenthesized_pointer_member_indirect_assignment_statement (); + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind postfix_op = tok.kind; + int step = 1; + + get_token (); + + if (!is_assignment_operator (tok.kind)) { + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (lhs) { + + if (lhs->pointer_depth > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (lhs->pointer_depth == 1) { + + deref_size = lhs->pointed_size & 0x1f; + step = lhs->pointed_size > 0 ? lhs->pointed_size : 1; + + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (get_global_symbol_pointer_depth (name) == 1) { + + deref_size = get_global_symbol_pointed_size (name) & 0x1f; + step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1; + + } + + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("ecx", name, DATA_PTR); + } + + emit_push_reg_now ("ecx"); + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", lhs->static_label, step); + } else { + fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->static_label); + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + char memref[64]; + + format_intel_ebp_offset (memref, sizeof (memref), lhs->offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, %d\n" : " %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step); + + } else { + fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->offset); + } + + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step); + } else { + fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name); + } + + } + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_pop_reg_now ("edx"); + emit_push_reg_now ("edx"); + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + enum token_kind postfix_op = TOK_EOF; + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int offset = 0; + int member_size = DATA_PTR & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int step; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (member); + free (name); + + return 1; + + } + + free (member); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_op = tok.kind; + get_token (); + + } + + if (!is_assignment_operator (tok.kind)) { + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (member_pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (member_pointer_depth == 1 && member_elem_size > 0) { + deref_size = member_elem_size & 0x1f; + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + step = member_elem_size > 0 ? member_elem_size : 1; + + if (state->ofp) { + + if (lhs) { + + if (member_op == TOK_ARROW) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("edx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("edx", lhs->offset); + } + + } + + } else { + + if (member_op == TOK_ARROW) { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } else { + emit_load_address_to_reg_now ("edx", name); + } + + } + + emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } else { + fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } + + } else { + fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset); + } + + } + + emit_push_reg_now ("ecx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("edx"); + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (!is_assignment_operator (tok.kind)) { + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (lhs) { + + if (lhs->pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (lhs->pointer_depth == 1) { + + /** + * Keep aggregate pointed-to sizes intact for assignments like + * *hashtab = old_hashtab; Masking with 0x1f corrupts struct + * copies whose size is greater than 31 bytes. + */ + deref_size = lhs->pointed_size; + + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (get_global_symbol_pointer_depth (name) == 1) { + deref_size = get_global_symbol_pointed_size (name); + } + + } + + if (deref_size == 0) { + deref_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + if (op == TOK_ASSIGN) { + + if (deref_size != (DATA_LLONG & 0x1f) && + emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, deref_size)) { + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + emit_push_reg_now ("edx"); + + if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1); + emit_pop_reg_now ("ecx"); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + if (deref_size == (DATA_LLONG & 0x1f)) { + emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx"); + } else { + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + } + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + +} + +static int source_starts_parenthesized_star_now (void) { + + const char *p = tok.caret ? tok.caret : tok.start; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t') { + p++; + } + + return *p == '*'; + +} + +static int parse_parenthesized_indirect_member_assignment_statement (void) { + + enum token_kind member_op; + enum token_kind op; + + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int saw_close = 0; + + if (tok.kind != TOK_LPAREN || !source_starts_parenthesized_star_now ()) { + return 0; + } + + get_token (); + + if (tok.kind != TOK_STAR) { + return 0; + } + + get_token (); + + /* + * Parse only the object expression inside the parenthesized dereference. + * For macro-expanded lvalues such as + * + * (*(__gtin()))->field = value; + * + * the normal assignment-expression loader can consume the complete + * "->field = value" tail as a value expression. That emits only + * member loads and drops the store. + */ + if (tok.kind == TOK_LPAREN) { + + int parens = 0; + char *inner_name = 0; + + const char *inner_start = 0; + const char *inner_caret = 0; + + unsigned long inner_line = 0; + struct local_symbol *inner_lhs; + + while (tok.kind == TOK_LPAREN) { + + parens++; + get_token (); + + } + + if (tok.kind == TOK_IDENT) { + + inner_name = xstrdup (tok.ident); + inner_start = tok.start; + inner_caret = tok.caret; + inner_line = get_line_number (); + + get_token (); + + if (tok.kind == TOK_LPAREN) { + emit_call_identifier_to_reg_now (inner_name, "edx", inner_start, inner_caret, inner_line); + } else { + + inner_lhs = find_local_symbol (inner_name); + + if (inner_lhs) { + + if (inner_lhs->is_static && inner_lhs->static_label) { + emit_load_global_to_reg ("edx", inner_lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", inner_lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", inner_name, DATA_PTR); + } + + } + + free (inner_name); + + while (parens > 0 && tok.kind == TOK_RPAREN) { + + saw_close = 1; + parens--; + + get_token (); + + } + + } else { + emit_load_assignment_rhs_expression_to_reg ("edx"); + } + + } else { + emit_load_assignment_rhs_expression_to_reg ("edx"); + } + + while (tok.kind == TOK_RPAREN) { + + saw_close = 1; + get_token (); + + } + + if (!saw_close) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) { + + int deref_size = DATA_INT & 0x1f; + int step = 1; + + if (rhs_last_pointer_depth > 1) { + + deref_size = DATA_PTR & 0x1f; + step = DATA_PTR & 0x1f; + + } else if (rhs_last_pointer_depth == 1 && rhs_last_pointed_size > 0) { + deref_size = rhs_last_pointed_size & 0x1f; + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + get_token (); + + if (state->ofp) { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s eax, %d\n", op == TOK_INCR ? "add" : "sub", step); + } else { + fprintf (state->ofp, " %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", step); + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } + + expect_semi_or_recover (); + return 1; + + } + + if (!is_assignment_operator (tok.kind)) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + return 1; + + } + + if (state->ofp) { + emit_load_deref_reg_now ("edx", DATA_PTR); + } + + member_op = tok.kind; + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &member_offset, &member_size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + free (member); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + free (member); + + if (!is_assignment_operator (tok.kind)) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + return 1; + + } + + op = tok.kind; + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + return 1; + +} + +static int token_text_looks_like_postfix_call_now (void) { + + const char *p; + + int saw_postfix; + int paren_depth; + int bracket_depth; + + if (tok.caret) { + p = tok.caret; + } else if (tok.start) { + p = tok.start; + } else { + return 0; + } + + saw_postfix = 0; + paren_depth = 0; + bracket_depth = 0; + + while (*p && *p != ';' && *p != '\n') { + + if (*p == '(') { + + if (saw_postfix && paren_depth == 0 && bracket_depth == 0) { + return 1; + } + + paren_depth++; + + } else if (*p == ')') { + + if (paren_depth > 0) { + paren_depth--; + } + + } else if (*p == '[') { + bracket_depth++; + } else if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') { + saw_postfix = 1; + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') { + + saw_postfix = 1; + p++; + + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') { + return 0; + } + + p++; + + } + + return 0; + +} + +static int parse_parenthesized_deref_subscript_statement (void) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + struct local_symbol *src; + + int global_index; + int pointer_depth; + int pointed_size; + + if (tok.kind != TOK_LPAREN || !source_starts_lparen_deref_subscript_at (tok.caret)) { + return 0; + } + + get_token (); + + if (tok.kind != TOK_STAR) { + return 0; + } + + get_token (); + + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + expect (TOK_RPAREN, ")"); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + pointer_depth = 0; + pointed_size = DATA_INT & 0x1f; + + if (src) { + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("eax", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("eax", src->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + emit_load_global_to_reg ("eax", name, DATA_PTR); + + } else { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + free (name); + return 1; + + } + + if (pointer_depth <= 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name); + + free (name); + return 1; + + } + + emit_load_deref_reg_now ("eax", DATA_PTR & 0x1f); + emit_handle_subscript_after_loaded_pointer_to_reg_now ("eax", pointer_depth - 1, pointed_size); + + free (name); + return 1; + +} + +static int parse_identifier_assignment_statement (void) { + + char *name; + int global_index; + + const char *name_start, *name_caret; + unsigned long name_line; + + enum token_kind op; + struct local_symbol *lhs; + + int lhs_size; + int lhs_is_floating; + + if (tok.kind != TOK_IDENT) { + return 0; + } + + if (token_text_looks_like_postfix_call_now ()) { + return 0; + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + if (tok.kind == TOK_COLON) { + + define_goto_label (name, name_line, name_start, name_caret); + + get_token (); + free (name); + + parse_statement (); + return 1; + + } + + if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + int member_elem_size = DATA_INT & 0x1f; + int member_pointer_depth = 0; + int member_is_floating = 0; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (name); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!find_member_info_ex_bounded (member, + member_op == TOK_DOT + ? (lhs ? lhs->size : (global_index >= 0 ? get_global_symbol_size (name) : 0)) + : (lhs ? lhs->pointed_size : (global_index >= 0 ? get_global_symbol_pointed_size (name) : 0)), + member_op == TOK_DOT + ? (lhs ? lhs->tag_name : (global_index >= 0 ? get_global_symbol_tag_name (name) : 0)) + : (lhs ? lhs->pointed_tag_name : 0), + &member_offset, &member_size, &member_elem_size, &member_pointer_depth, 0, &member_is_floating)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return 1; + + } + + free (member); + + if (!is_assignment_operator (tok.kind)) { + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + enum token_kind postfix_op = tok.kind; + int step = member_pointer_depth > 0 && member_elem_size > 0 ? member_elem_size : 1; + + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + if (state->ofp) { + + if (member_op == TOK_ARROW) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("edx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("edx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("edx", name); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *opname = postfix_op == TOK_INCR ? "add" : "sub"; + const char *opsize = member_size == 1 ? "byte" : (member_size == 2 ? "word" : "dword"); + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s [edx + %d], %d\n", opname, opsize, member_offset, step); + } else { + fprintf (state->ofp, " %s %s ptr [edx + %d], %d\n", opname, opsize, member_offset, step); + } + + } else { + + const char *suffix = member_size == 1 ? "b" : (member_size == 2 ? "w" : "l"); + fprintf (state->ofp, " %s%s $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", suffix, step, member_offset); + + } + + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + op = tok.kind; + get_token (); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + if (state->ofp) { + + if (member_op == TOK_ARROW) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("edx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("edx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("edx", name); + } + + } + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + + if (member_is_floating || token_is_floating_constant_now ()) { + emit_load_floating_rhs_expression_now (member_size); + } else { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } + + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + + if (tok.kind == TOK_TILDE) { + + int64_s rhs_const; + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + rhs_const = const64_from_current_foldable_expr (); + expect (TOK_RPAREN, ")"); + + } else { + rhs_const = const64_from_current_foldable_expr (); + } + + rhs_const.low = (~rhs_const.low) & U32_MASK; + rhs_const.high = (~rhs_const.high) & U32_MASK; + + emit_load_const32_to_reg_now ("edx", rhs_const); + + } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) { + + int64_s rhs_const = const64_from_current_foldable_expr (); + emit_load_const32_to_reg_now ("edx", rhs_const); + + } else { + emit_load_assignment_rhs_expression_to_reg ("edx"); + } + + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + if ((member_is_floating || token_is_floating_constant_now ()) && op == TOK_ASSIGN) { + emit_store_floating_member_to_addr_reg_now ("edx", member_offset, member_size); + } else { + emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size); + } + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (tok.kind == TOK_LBRACK) { + + int elem_size = DATA_INT & 0x1f; + int elem_pointer_depth = 0; + int elem_pointed_size = DATA_INT & 0x1f; + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (lhs) { + + elem_size = lhs->is_array ? (lhs->pointer_depth ? DATA_PTR : lhs->pointed_size) : + (lhs->pointer_depth > 1 ? DATA_PTR : lhs->pointed_size); + + elem_pointer_depth = lhs->pointer_depth > 0 ? lhs->pointer_depth - 1 : 0; + elem_pointed_size = lhs->pointed_size; + + } else { + + elem_size = get_global_symbol_array (name) ? + (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) : + (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name)); + + elem_pointer_depth = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointer_depth (name) - 1 : 0; + elem_pointed_size = get_global_symbol_pointed_size (name); + + } + + if ((elem_size & 0x1f) == 0) { + elem_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (lhs) { + + if (lhs->is_array) { + + if (lhs->is_static && lhs->static_label) { + emit_load_symbol_address_to_reg_now ("edx", lhs->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now ("edx", 0, lhs->offset, 1); + } + + } else if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else if (get_global_symbol_array (name)) { + emit_load_symbol_address_to_reg_now ("edx", name, 0, 0); + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size); + + } else { + emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size); + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + get_token (); + + if (state->ofp) { + + int inc_amount = (elem_pointer_depth > 0 && (elem_pointed_size & 0x1f) > 0) ? + (elem_pointed_size & 0x1f) : 1; + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s eax, %d\n", op == TOK_INCR ? "add" : "sub", inc_amount); + } else { + fprintf (state->ofp, " %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", inc_amount); + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size); + + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (!is_assignment_operator (tok.kind)) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + op = tok.kind; + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (!is_assignment_operator (tok.kind)) { + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret); + + expect_semi_or_recover (); + free (name); + + return 1; + + } + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (name)) { + ensure_global_function_symbol (name, name_start, name_caret, name_line); + } + + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + + if (tok.kind == TOK_LBRACK) { + + int elem_size = get_global_symbol_pointed_size (name); + + if ((elem_size & 0x1f) == 0) { + elem_size = DATA_INT & 0x1f; + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, eax\n"); + } else { + fprintf (state->ofp, " movl %%eax, %%edx\n"); + } + + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size); + + if (is_assignment_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (state->ofp) { + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_PTR & 0x1f; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &member_offset, &member_size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + free (member); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + free (member); + + if (member_op == TOK_DOT && state->ofp) { + emit_load_deref_reg_now ("eax", DATA_PTR); + } + + if (is_assignment_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, eax\n"); + } else { + fprintf (state->ofp, " movl %%eax, %%edx\n"); + } + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + free (name); + + return 1; + + } + + if (tok.kind == TOK_LPAREN) { + + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps; + FILE *arg_tmp_ofp; + FILE *arg_saved_ofp; + + int argc = 0; + int i; + int ch; + int total_arg_bytes = 0; + + if (state->ofp) { + + emit_load_member_from_addr_reg_now ("ecx", "eax", member_offset, DATA_PTR & 0x1f); + emit_push_reg_now ("ecx"); + + } + + get_token (); + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + arg_tmp_ofp = 0; + arg_saved_ofp = 0; + + if (state->ofp) { + + arg_tmp_ofp = tmpfile (); + + if (arg_tmp_ofp) { + arg_saved_ofp = state->ofp; + state->ofp = arg_tmp_ofp; + } + + } + + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (state->ofp) { + emit_push_reg_now ("eax"); + } + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + state->ofp = arg_saved_ofp; + + new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1)); + + if (new_arg_tmp_ofps) { + + arg_tmp_ofps = new_arg_tmp_ofps; + arg_tmp_ofps[argc] = arg_tmp_ofp; + arg_tmp_ofp = 0; + + } + + } + + if (arg_tmp_ofp) { + fclose (arg_tmp_ofp); + } + + argc++; + total_arg_bytes += DATA_PTR & 0x1f; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + if (state->ofp) { + + for (i = argc - 1; i >= 0; i--) { + + if (arg_tmp_ofps && arg_tmp_ofps[i]) { + + fseek (arg_tmp_ofps[i], 0, SEEK_SET); + + while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { + fputc (ch, state->ofp); + } + + fclose (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + if (arg_tmp_ofps) { + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov ecx, dword [esp + %d]\n", total_arg_bytes); + } else { + fprintf (state->ofp, " mov ecx, dword ptr [esp + %d]\n", total_arg_bytes); + } + + fprintf (state->ofp, " call ecx\n"); + fprintf (state->ofp, " add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f)); + + } else { + + fprintf (state->ofp, " movl %d(%%esp), %%ecx\n", total_arg_bytes); + fprintf (state->ofp, " call *%%ecx\n"); + fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f)); + + } + + } + + expect_semi_or_recover (); + free (name); + + return 1; + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect_semi_or_recover (); + free (name); + + return 1; + + } + + expect_semi_or_recover (); + free (name); + + return 1; + + } + + if (!find_local_symbol (name) && find_global_symbol (name) < 0) { + + int64_s ignored; + + if (!resolve_enum_constant (name, &ignored)) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + } + + free (name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + return 1; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + if (state->ofp) { + + lhs_size = lhs ? lhs->size : get_global_symbol_size (name); + lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name); + + if (op == TOK_ASSIGN && lhs_size > (DATA_LLONG & 0x1f) && tok.kind == TOK_IDENT) { + + char *rhs_name = xstrdup (tok.ident); + + struct local_symbol *rhs_sym; + int rhs_global_index; + + get_token (); + + rhs_sym = find_local_symbol (rhs_name); + rhs_global_index = find_global_symbol (rhs_name); + + if (rhs_sym || rhs_global_index >= 0) { + + if (tok.kind == TOK_LPAREN && rhs_global_index >= 0 && get_global_symbol_kind (rhs_name) == GLOBAL_SYMBOL_FUNCTION) { + + pending_struct_return_lhs = lhs; + pending_struct_return_global_name = lhs ? 0 : name; + + emit_call_identifier_to_reg_now (rhs_name, "eax", name_start, name_caret, name_line); + + } else if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) { + + if (emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, name_start, name_caret, name_line)) { + + emit_load_symbol_address_for_copy_now ("edx", lhs, name); + emit_copy_fixed_size_now (lhs_size); + + } + + } else { + emit_memcpy_symbol_to_symbol_now (lhs, name, rhs_sym, rhs_name, lhs_size); + } + + } else { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", rhs_name); + } + + free (rhs_name); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (lhs_is_floating) { + + if (!floating_assignment_operator_supported_now (op)) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator"); + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + } else { + + if (op == TOK_ASSIGN) { + emit_load_floating_rhs_expression_now (lhs_size); + } else { + + emit_load_floating_symbol_now (lhs, name, lhs_size); + emit_load_floating_rhs_expression_now (lhs_size); + emit_floating_binary_now (op); + + } + + emit_store_floating_symbol_now (lhs, name, lhs_size); + + } + + } else if (lhs_size == (DATA_LLONG & 0x1f)) { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("eax", "edx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "eax", "edx"); + } + + } else { + emit_load_global64_to_pair ("eax", "edx", name); + } + + /* + * Compound assignments need the complete RHS expression. + * Using emit_load_assignment_rhs_to_pair() only consumes one + * primary operand, so e.g. + * + * final_value += symbol->frag->address + left_value; + * + * leaves the second + operand for the statement parser and + * reports "expected ;". Evaluate the full RHS in eax:edx, + * copy it to the RHS pair ebx:ecx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("eax"); + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + emit_mov_reg_to_reg_now ("ebx", "eax"); + emit_mov_reg_to_reg_now ("ecx", "edx"); + + emit_pop_reg_now ("edx"); + emit_pop_reg_now ("eax"); + + emit_preserve_assignment64_regs (op); + emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + emit_restore_assignment64_regs (op); + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_pair_to_global64 (lhs->static_label, "eax", "edx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "eax", "edx"); + } + + } else { + emit_store_pair_to_global64 (name, "eax", "edx"); + } + + } else { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("eax", lhs->static_label, lhs->size); + } else { + emit_load_local_to_reg ("eax", lhs->offset, lhs->size); + } + + } else { + emit_load_global_to_reg ("eax", name, lhs_size); + } + + /* + * Compound assignments still need the full RHS expression, + * not just a single operand. Otherwise cases such as: + * + * processed += (int)(t - stream->upto) - 1; + * + * leave the trailing "- 1" unconsumed and the statement + * parser reports "expected ;". Preserve the current LHS + * value in eax while the RHS expression is parsed, because + * RHS binary-expression code may use eax internally even + * when the requested result register is edx. + */ + emit_push_reg_now ("eax"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + + emit_scale_reg_for_pointer_compound_assignment_now ("edx", lhs, name, op); + emit_pop_reg_now ("eax"); + + emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_reg_to_global (lhs->static_label, lhs->size, "eax"); + } else { + emit_store_reg_to_local (lhs->offset, lhs->size, "eax"); + } + + } else { + emit_store_reg_to_global (name, lhs_size, "eax"); + } + + } + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + +} + +static void emit_statement_label_raw (int label) { + + if (!state->ofp) { + return; + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, "L%d:\n", label); + } else { + fprintf (state->ofp, ".L%d:\n", label); + } + +} + +static void flush_pending_statement_labels (void) { + + int i; + int count = pending_statement_label_count; + + /** + * A queued case/default/C label marks the next statement. If the + * previous statement was a return, its jump to the common epilogue may + * still be pending. Emit that jump before placing the next label, + * otherwise the label lands between the return value setup and the + * deferred jump. + * + * Also flush the deferred return before any other emitted control-flow + * boundary even when there are no queued labels. A switch body is emitted + * into a temporary stream and then replayed before the switch break label; + * if the last case returns, there may be no following case/default label to + * force this flush. + */ + if (pending_return_jump) { + emit_pending_return_jump (); + } + + if (count <= 0) { + return; + } + + pending_statement_label_count = 0; + + for (i = 0; i < count; i++) { + emit_statement_label_raw (pending_statement_labels[i]); + } + +} + +static void emit_statement_label (int label) { + + flush_pending_statement_labels (); + emit_statement_label_raw (label); + +} + +static void emit_statement_jump (int label) { + + flush_pending_statement_labels (); + + if (!state->ofp) { + return; + } + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + fprintf (state->ofp, " jmp L%d\n", label); + } else { + fprintf (state->ofp, " jmp .L%d\n", label); + } + +} + +static int token_is_integer_constant_now (enum token_kind k) { + return k == TOK_CINT || k == TOK_CLONG || k == TOK_CLLONG || k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1); +} + +static int token_is_integer_unsigned_constant_now (enum token_kind k) { + return k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1); +} + +#define MAX_INLINE_ASM_INPUTS 16 + +struct inline_asm_input_operand { + + char constraint[16]; + char subst[64]; + int restore_reg; + const char *restore_name; + +}; + +static void inline_asm_copy_trimmed_text (char *dst, size_t dst_size, const char *start, const char *end) { + + size_t len; + + if (!dst || dst_size == 0) { + return; + } + + dst[0] = 0; + + if (!start || !end || end < start) { + return; + } + + while (start < end && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n')) { + start++; + } + + while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) { + end--; + } + + len = (size_t) (end - start); + + if (len >= dst_size) { + len = dst_size - 1; + } + + memcpy (dst, start, len); + dst[len] = 0; + +} + +static void inline_asm_unquote_string_token (char *dst, size_t dst_size) { + + const char *s; + char quote; + size_t n = 0; + + if (!dst || dst_size == 0) { + return; + } + + dst[0] = 0; + + if (!is_string_token ()) { + return; + } + + s = tok.ident; + + if (tok.kind == TOK_LSTR && *s == 'L') { + s++; + } + + quote = *s; + + if (quote != '"') { + return; + } + + s++; + + while (*s && *s != quote && n + 1 < dst_size) { + + if (*s == '\\' && s[1]) { + + s++; + + switch (*s) { + + case 'n': + + dst[n++] = '\n'; s++; + break; + + case 'r': + + dst[n++] = '\r'; s++; + break; + + case 't': + + dst[n++] = '\t'; s++; + break; + + case '\\': + + dst[n++] = '\\'; s++; + break; + + case '"': + + dst[n++] = '"'; s++; + break; + + default: + + dst[n++] = *s++; + break; + + } + + } else { + dst[n++] = *s++; + } + + } + + dst[n] = 0; + +} + +static const char *inline_asm_constraint_reg32 (const char *constraint) { + + if (!constraint) { + return 0; + } + + if (strchr (constraint, 'a')) { + return "eax"; + } + + if (strchr (constraint, 'b')) { + return "ebx"; + } + + if (strchr (constraint, 'c')) { + return "ecx"; + } + + if (strchr (constraint, 'd')) { + return "edx"; + } + + if (strchr (constraint, 'S')) { + return "esi"; + } + + if (strchr (constraint, 'D')) { + return "edi"; + } + + return 0; + +} + +static const char *inline_asm_reg_for_template (const char *reg32) { + + if (!reg32) { + return ""; + } + + if (strcmp (reg32, "eax") == 0) { + return "al"; + } + + if (strcmp (reg32, "ebx") == 0) { + return "bl"; + } + + if (strcmp (reg32, "ecx") == 0) { + return "cl"; + } + + if (strcmp (reg32, "edx") == 0) { + return "dx"; + } + + if (strcmp (reg32, "esi") == 0) { + return "esi"; + } + + if (strcmp (reg32, "edi") == 0) { + return "edi"; + } + + return reg32; + +} + +static int inline_asm_reg_needs_restore (const char *reg32) { + return reg32 && (strcmp (reg32, "ebx") == 0 || strcmp (reg32, "esi") == 0 || strcmp (reg32, "edi") == 0); +} + +static int inline_asm_template_is_out (const char *templ) { + + if (!templ) { + return 0; + } + + while (*templ == ' ' || *templ == '\t') { + templ++; + } + + return strncmp (templ, "outb ", 5) == 0 || strncmp (templ, "outw ", 5) == 0 || + strncmp (templ, "outl ", 5) == 0 || strncmp (templ, "out ", 4) == 0; + +} + +static int inline_asm_is_reg32_name (const char *s, size_t len) { + + return (len == 3 && strncmp (s, "eax", 3) == 0) || + (len == 3 && strncmp (s, "ebx", 3) == 0) || + (len == 3 && strncmp (s, "ecx", 3) == 0) || + (len == 3 && strncmp (s, "edx", 3) == 0) || + (len == 3 && strncmp (s, "esi", 3) == 0) || + (len == 3 && strncmp (s, "edi", 3) == 0); + +} + +static int inline_asm_rewrite_narrow_movzx (char *line, size_t line_size) { + + char rewritten[512]; + + const char *p; + const char *reg_start; + const char *reg_end; + const char *src; + + size_t reg_len; + size_t indent_len; + + if (!line || line_size == 0 || !(state->syntax & ASM_SYNTAX_INTEL)) { + return 0; + } + + p = line; + + while (*p == ' ' || *p == '\t') { + p++; + } + + indent_len = (size_t) (p - line); + + if (strncmp (p, "mov", 3) != 0 || (p[3] != ' ' && p[3] != '\t')) { + return 0; + } + + p += 3; + + while (*p == ' ' || *p == '\t') { + p++; + } + + reg_start = p; + + while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) { + p++; + } + + reg_end = p; + reg_len = (size_t) (reg_end - reg_start); + + if (!inline_asm_is_reg32_name (reg_start, reg_len)) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p != ',') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t') { + p++; + } + + src = p; + + /** + * Only change true narrow memory loads. This deliberately does not + * touch register/register moves, immediates, or lines that have already + * been rewritten. + */ + if (strncmp (src, "byte ptr ", 9) != 0 && strncmp (src, "word ptr ", 9) != 0) { + return 0; + } + + if (indent_len + 6 + reg_len + 2 + strlen (src) + 1 >= sizeof (rewritten)) { + return 0; + } + + memcpy (rewritten, line, indent_len); + rewritten[indent_len] = 0; + + strcat (rewritten, "movzx "); + strncat (rewritten, reg_start, reg_len); + strcat (rewritten, ", "); + strcat (rewritten, src); + + inline_copy_string (line, rewritten, line_size); + return 1; + +} + +static const char *inline_asm_intel_ptr_name (int size) { + + if (size <= 1) { + return "byte"; + } + + if (size == 2) { + return "word"; + } + + if (size == 8) { + return "qword"; + } + + return "dword"; + +} + +static int inline_asm_format_identifier_operand (char *dst, size_t dst_size, const char *name) { + + struct local_symbol *sym; + char memref[64]; + const char *asm_name; + + if (!dst || dst_size == 0 || !name || !*name) { + return 0; + } + + dst[0] = 0; + sym = find_local_symbol (name); + + if (sym) { + + if (sym->is_static && sym->static_label) { + + asm_name = asm_global_symbol_name (sym->static_label); + + if (state->syntax & ASM_SYNTAX_INTEL) { + sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), asm_name); + } else { + sprintf (dst, "%s", asm_name); + } + + return 1; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), sym->offset); + sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), memref); + + } else { + sprintf (dst, "%ld(%%ebp)", sym->offset); + } + + return 1; + + } + + if (find_global_symbol (name) >= 0) { + + asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_INTEL) { + sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (get_global_symbol_size (name)), asm_name); + } else { + sprintf (dst, "%s", asm_name); + } + + return 1; + + } + + return 0; + +} + +static void inline_asm_emit_input_load (const char *constraint, int input_index, struct inline_asm_input_operand *inputs, const char *templ) { + + char expr_text[64]; + + const char *expr_start; + const char *expr_end; + const char *reg32; + const char *subst; + + expr_text[0] = 0; + expr_start = tok.caret; + + inputs[input_index].restore_reg = 0; + inputs[input_index].restore_name = 0; + + if (strchr (constraint, 'N')) { + + if (token_is_integer_constant_now (tok.kind)) { + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + expr_end = tok.caret; + + inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end); + inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst)); + + return; + + } + + if (inline_asm_template_is_out (templ)) { + + /** + * GCC's "N" constraint is really an 8-bit immediate port. + * For the OUT templates, allow a non-constant here as a + * convenience and lower it through DX, because x86 OUT cannot + * encode a variable port as an immediate. + */ + emit_load_assignment_rhs_expression_to_reg ("edx"); + inline_copy_string (inputs[input_index].subst, "dx", sizeof (inputs[input_index].subst)); + + return; + + } + + if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) { + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst)); + + return; + + } + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + expr_end = tok.caret; + + inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end); + inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst)); + + return; + + } + + reg32 = inline_asm_constraint_reg32 (constraint); + + if (reg32) { + + subst = inline_asm_reg_for_template (reg32); + + if (inline_asm_reg_needs_restore (reg32) && state->ofp) { + + fprintf (state->ofp, " push %s\n", reg32); + + inputs[input_index].restore_reg = 1; + inputs[input_index].restore_name = reg32; + + } + + if (token_is_integer_constant_now (tok.kind)) { + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + expr_end = tok.caret; + + inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end); + + if (state->ofp) { + fprintf (state->ofp, " mov %s, %s\n", subst, expr_text[0] ? expr_text : "0"); + } + + } else { + emit_load_assignment_rhs_expression_to_reg (reg32); + } + + inline_copy_string (inputs[input_index].subst, subst, sizeof (inputs[input_index].subst)); + return; + + } + + if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) { + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst)); + + return; + + } + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + expr_end = tok.caret; + + inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end); + inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst)); + +} + +static void inline_asm_emit_template (const char *templ, struct inline_asm_input_operand *inputs, int input_count) { + + char line[512]; + size_t n = 0; + + const char *p; + + if (!state->ofp || !templ) { + return; + } + + for (p = templ; *p && n + 1 < sizeof (line); p++) { + + if (*p == '%' && p[1] == '%') { + + if (state->syntax & ASM_SYNTAX_INTEL) { + p++; + } else { + + line[n++] = '%'; + p++; + + } + + } else if (*p == '%' && p[1] >= '0' && p[1] <= '9') { + + int index = p[1] - '0'; + const char *subst = ""; + + if (index >= 0 && index < input_count) { + subst = inputs[index].subst; + } + + while (*subst && n + 1 < sizeof (line)) { + line[n++] = *subst++; + } + + p++; + + } else { + line[n++] = *p; + } + + } + + line[n] = 0; + + if ((strncmp (line, "outb ", 5) == 0 || strncmp (line, "outw ", 5) == 0 || strncmp (line, "outl ", 5) == 0 || strncmp (line, "out ", 4) == 0)) { + + char *args; + char *comma; + char *lhs; + char *rhs; + + args = strchr (line, ' '); + + if (args) { + + args++; + comma = strchr (args, ','); + + if (comma) { + + *comma = 0; + + lhs = args; + rhs = comma + 1; + + while (*lhs == ' ' || *lhs == '\t') { + lhs++; + } + + while (*rhs == ' ' || *rhs == '\t') { + rhs++; + } + + fprintf (state->ofp, " out %s, %s\n", rhs, lhs); + + while (input_count-- > 0) { + + if (inputs[input_count].restore_reg && inputs[input_count].restore_name) { + fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name); + } + + } + + return; + + } + + } + + } + + if (line[0]) { + + inline_asm_rewrite_narrow_movzx (line, sizeof (line)); + fprintf (state->ofp, " %s\n", line); + + } + + while (input_count-- > 0) { + + if (inputs[input_count].restore_reg && inputs[input_count].restore_name) { + fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name); + } + + } + +} + +static int parse_inline_asm_statement (void) { + + char templ[256]; + + struct inline_asm_input_operand inputs[MAX_INLINE_ASM_INPUTS]; + int input_count = 0; + + if (tok.kind != TOK_ASM) { + return 0; + } + + memset (inputs, 0, sizeof (inputs)); + + get_token (); + expect (TOK_LPAREN, "("); + + if (!is_string_token ()) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm template string"); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + return 1; + + } + + inline_asm_unquote_string_token (templ, sizeof (templ)); + get_token (); + + if (_accept (TOK_COLON)) { + + if (tok.kind != TOK_COLON) { + skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF); + } + + if (_accept (TOK_COLON)) { + + while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) { + + char constraint[16]; + + if (!is_string_token ()) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm input constraint string"); + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + break; + + } + + inline_asm_unquote_string_token (constraint, sizeof (constraint)); + get_token (); + + expect (TOK_LPAREN, "("); + + if (input_count < MAX_INLINE_ASM_INPUTS) { + + inline_copy_string (inputs[input_count].constraint, constraint, sizeof (inputs[input_count].constraint)); + inline_asm_emit_input_load (constraint, input_count, inputs, templ); + + input_count++; + + } else { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + expect (TOK_RPAREN, ")"); + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + } + + expect (TOK_RPAREN, ")"); + expect (TOK_SEMI, ";"); + + inline_asm_emit_template (templ, inputs, input_count); + return 1; + +} + +static int token_is_const_condition_operand_now (void) { + return token_is_sizeof_keyword () || token_is_integer_constant_now (tok.kind); +} + +static int token_is_const_floating_condition_operand_now (void) { + return token_is_floating_constant_now (); +} + +static int ident_char_now (int ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_'; +} + +static int ident_start_now (int ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_'; +} + +static int prefix_incdec_target_is_floating_now (void) { + + const char *p; + const char *q; + + char *name; + int len; + + struct local_symbol *src; + int ret = 0; + + if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) { + return 0; + } + + p = tok.caret; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!ident_start_now ((unsigned char) *p)) { + return 0; + } + + q = p + 1; + + while (ident_char_now ((unsigned char) *q)) { + q++; + } + + len = (int) (q - p); + name = xmalloc ((unsigned long) len + 1); + + memcpy (name, p, (unsigned long) len); + name[len] = 0; + + src = find_local_symbol (name); + + if (src) { + ret = src->is_floating ? 1 : 0; + } else if (find_global_symbol (name) >= 0) { + ret = get_global_symbol_floating (name) ? 1 : 0; + } + + free (name); + return ret; + +} + +static int source_parenthesized_floating_condition_now (const char *p) { + + char name[128]; + int i; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p != '(') { + return 0; + } + + while (*p == '(' || *p == ' ' || *p == '\t') { + p++; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + i = 0; + + while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && i < (int) sizeof (name) - 1) { + name[i++] = *p++; + } + + name[i] = 0; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p == '.') { + return 0; + } + + if (*p == '(' && find_global_symbol (name) >= 0 && get_global_symbol_floating (name)) { + return 1; + } + + return 0; + +} + +static int rhs_current_operand_is_floating_now (void) { + + if (token_is_floating_constant_now ()) { + return 1; + } + + if (tok.kind == TOK_LPAREN && (source_parenthesized_floating_condition_now (tok.caret) || source_parenthesized_floating_condition_now (tok.start))) { + return 1; + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + return prefix_incdec_target_is_floating_now (); + } + + if (tok.kind == TOK_IDENT && tok.ident) { + + struct local_symbol *src = find_local_symbol (tok.ident); + const char *p = tok.caret ? tok.caret + tok.len : 0; + + while (p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + p++; + } + + if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) { + return get_global_symbol_floating (tok.ident) ? 1 : 0; + } + + if (p && ((p[0] == '-' && p[1] == '>') || p[0] == '.')) { + + const char *q = p + (p[0] == '-' ? 2 : 1); + char member[128]; + + int n = 0; + int offset = 0; + int size = 0; + int elem_size = 0; + int pointer_depth = 0; + int is_array = 0; + int is_floating = 0; + int base_size = 0; + + const char *base_tag_name = 0; + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + while (((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') && n < (int) sizeof (member) - 1) { + member[n++] = *q++; + } + + member[n] = '\0'; + + if (n > 0) { + + if (p[0] == '-') { + + if (src) { + + base_size = src->pointed_size; + base_tag_name = src->pointed_tag_name; + + } else if (find_global_symbol (tok.ident) >= 0) { + + base_size = get_global_symbol_pointed_size (tok.ident); + base_tag_name = get_global_symbol_tag_name (tok.ident); + + } + + } else { + + if (src) { + + base_size = src->size; + base_tag_name = src->tag_name; + + } else if (find_global_symbol (tok.ident) >= 0) { + + base_size = get_global_symbol_size (tok.ident); + base_tag_name = get_global_symbol_tag_name (tok.ident); + + } + + } + + if (find_member_info_ex_bounded (member, base_size, base_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) { + return is_floating ? 1 : 0; + } + + } + + } + + if (src) { + return src->is_floating ? 1 : 0; + } + + if (find_global_symbol (tok.ident) >= 0) { + return get_global_symbol_floating (tok.ident) ? 1 : 0; + } + + } + + return 0; + +} + +static int64_s floating_ld_to_bits_now (int size, long double value) { + + int64_s r; + + unsigned long bits32; + unsigned char bytes[8]; + + int i; + + r.low = 0; + r.high = 0; + + if (size == (DATA_FLOAT & 0x1f)) { + + float f = (float) value; + + bits32 = 0; + + memcpy (&bits32, &f, sizeof (f)); + r.low = bits32; + + return r; + + } + + { + + double d = (double) value; + + memset (bytes, 0, sizeof (bytes)); + memcpy (bytes, &d, sizeof (d)); + + for (i = 0; i < 4; i++) { + r.low |= ((unsigned long) bytes[i]) << (i * 8); + } + + for (i = 0; i < 4; i++) { + r.high |= ((unsigned long) bytes[i + 4]) << (i * 8); + } + + return r; + + } + +} + +static void emit_load_floating_ld_now (int size, long double value) { + emit_load_floating_const_bits_now (size, floating_ld_to_bits_now (size, value)); +} + +static int int64_statement_truth_value (int64_s v) { + return v.low != 0 || v.high != 0; +} + +static long statement_int64_signed_high (int64_s v) { + + unsigned long h = v.high & U32_MASK; + + if (h & 0x80000000UL) { + return -((long) ((~h + 1UL) & U32_MASK)); + } + + return (long) h; + +} + +static int statement_cmp_const64_unsigned (int64_s left, int64_s right) { + + unsigned long lh = left.high & U32_MASK; + unsigned long rh = right.high & U32_MASK; + unsigned long ll = left.low & U32_MASK; + unsigned long rl = right.low & U32_MASK; + + if (lh < rh) return -1; + if (lh > rh) return 1; + if (ll < rl) return -1; + if (ll > rl) return 1; + + return 0; + +} + +static int statement_cmp_const64_signed (int64_s left, int64_s right) { + + long lh = statement_int64_signed_high (left); + long rh = statement_int64_signed_high (right); + + unsigned long ll = left.low & U32_MASK; + unsigned long rl = right.low & U32_MASK; + + if (lh < rh) return -1; + if (lh > rh) return 1; + if (ll < rl) return -1; + if (ll > rl) return 1; + + return 0; + +} + +static int statement_compare_const64_true (int64_s left, enum token_kind op, int64_s right, int is_unsigned) { + + int c = is_unsigned ? statement_cmp_const64_unsigned (left, right) : statement_cmp_const64_signed (left, right); + + switch (op) { + + case TOK_LESS: + + return c < 0; + + case TOK_LTEQ: + + return c <= 0; + + case TOK_GREATER: + + return c > 0; + + case TOK_GTEQ: + + return c >= 0; + + case TOK_EQEQ: + + return c == 0; + + case TOK_NOTEQ: + + return c != 0; + + default: + + return int64_statement_truth_value (left); + + } + +} + +static int rhs_current_operand_is_unsigned_now (void) { + + if (token_is_sizeof_keyword ()) { + return 1; + } + + if (token_is_integer_unsigned_constant_now (tok.kind)) { + return 1; + } + + if (tok.kind == TOK_IDENT && tok.ident) { + + struct local_symbol *src = find_local_symbol (tok.ident); + + if (src) { + return src->is_unsigned ? 1 : 0; + } + + if (find_global_symbol (tok.ident) >= 0) { + return get_global_symbol_unsigned (tok.ident) ? 1 : 0; + } + + } + + return 0; + +} + +static int token_is_statement_compare_operator (enum token_kind k) { + return k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER || k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ; +} + +static int statement_condition_ident_call_at (const char *p) { + + int len; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + len = (int) strlen (tok.ident); + + while (*p && *p != '\n') { + + if ((p == tok.start || !ident_char_now ((unsigned char) p[-1])) && strncmp (p, tok.ident, (unsigned long) len) == 0 && !ident_char_now ((unsigned char) p[len])) { + + p += len; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '('; + + } + + p++; + + } + + return 0; + +} + +static int statement_condition_starts_with_ident_call_now (void) { + + if (statement_condition_ident_call_at (tok.caret)) { + return 1; + } + + if (statement_condition_ident_call_at (tok.start)) { + return 1; + } + + return 0; + +} + +static const char *statement_false_jump_mnemonic (enum token_kind op, int is_unsigned) { + + switch (op) { + + case TOK_LESS: + + return is_unsigned ? "jae" : "jge"; + + case TOK_LTEQ: + + return is_unsigned ? "ja" : "jg"; + + case TOK_GREATER: + + return is_unsigned ? "jbe" : "jle"; + + case TOK_GTEQ: + + return is_unsigned ? "jb" : "jl"; + + case TOK_EQEQ: + + return "jne"; + + case TOK_NOTEQ: + + return "je"; + + default: + + return "jz"; + + } + +} + +static void emit_statement_const32_to_eax (int64_s v) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %lu\n", v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%eax\n", v.low & U32_MASK); + } + +} + +static void emit_statement_cmp_eax_edx_jump_if_false (enum token_kind op, int is_unsigned, int label) { + + const char *jmp; + + if (!state->ofp) { + return; + } + + jmp = statement_false_jump_mnemonic (op, is_unsigned); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " cmp eax, edx\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " %s L%d\n" : " %s .L%d\n"), jmp, label); + + } else { + + fprintf (state->ofp, " cmpl %%edx, %%eax\n"); + fprintf (state->ofp, " %s .L%d\n", jmp, label); + + } + +} + +static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned) { + + int true_label; + int false_label; + int end_label; + + if (!state->ofp) { + return; + } + + true_label = anon_label++; + false_label = anon_label++; + end_label = anon_label++; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + + switch (op) { + + case TOK_LESS: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jb L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jbe L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " ja L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jae L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, " jne L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " je L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, " jne L%d\n", true_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jne L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " test edx, edx\n"); + fprintf (state->ofp, " jnz L%d\n", true_label); + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, " jnz L%d\n", true_label); + break; + + } + + fprintf (state->ofp, "L%d:\n", false_label); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " jmp L%d\n", end_label); + fprintf (state->ofp, "L%d:\n", true_label); + fprintf (state->ofp, " mov eax, 1\n"); + fprintf (state->ofp, "L%d:\n", end_label); + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + switch (op) { + + case TOK_LESS: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jb .L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jbe .L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " ja .L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jae .L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, " jne .L%d\n", false_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " je .L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmp edx, ecx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + fprintf (state->ofp, " cmp eax, ebx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " test edx, edx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + break; + + } + + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xor eax, eax\n"); + fprintf (state->ofp, " jmp .L%d\n", end_label); + fprintf (state->ofp, ".L%d:\n", true_label); + fprintf (state->ofp, " mov eax, 1\n"); + fprintf (state->ofp, ".L%d:\n", end_label); + + } else { + + switch (op) { + + case TOK_LESS: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " jb .L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " jbe .L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " ja .L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label); + fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " jae .L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, " jne .L%d\n", false_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " je .L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmpl %%ecx, %%edx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + fprintf (state->ofp, " cmpl %%ebx, %%eax\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " testl %%edx, %%edx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + break; + + } + + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xorl %%eax, %%eax\n"); + fprintf (state->ofp, " jmp .L%d\n", end_label); + fprintf (state->ofp, ".L%d:\n", true_label); + fprintf (state->ofp, " movl $1, %%eax\n"); + fprintf (state->ofp, ".L%d:\n", end_label); + + } + +} + +static const char *statement_float_false_jump_mnemonic (enum token_kind op) { + + switch (op) { + + case TOK_LESS: + + return "jae"; + + case TOK_LTEQ: + + return "ja"; + + case TOK_GREATER: + + return "jbe"; + + case TOK_GTEQ: + + return "jb"; + + case TOK_EQEQ: + + return "jne"; + + case TOK_NOTEQ: + + return "je"; + + default: + + return "jz"; + + } + +} + +static void emit_statement_floating_compare_jump_if_false (enum token_kind op, int label) { + + const char *jmp; + + if (!state->ofp) { + return; + } + + jmp = statement_float_false_jump_mnemonic (op); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " fxch st1\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, " %s L%d\n", jmp, label); + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " fxch st(1)\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " %s L%d\n" : " %s .L%d\n"), jmp, label); + + } else { + + fprintf (state->ofp, " fxch %%st(1)\n"); + fprintf (state->ofp, " fcompp\n"); + fprintf (state->ofp, " fnstsw %%ax\n"); + fprintf (state->ofp, " sahf\n"); + fprintf (state->ofp, " %s .L%d\n", jmp, label); + + } + +} + +static int statement_compare_floating_const_true (long double left, enum token_kind op, long double right) { + + switch (op) { + + case TOK_LESS: + + return left < right; + + case TOK_LTEQ: + + return left <= right; + + case TOK_GREATER: + + return left > right; + + case TOK_GTEQ: + + return left >= right; + + case TOK_EQEQ: + + return left == right; + + case TOK_NOTEQ: + + return left != right; + + default: + + return left != 0.0L; + + } + +} + +static void emit_statement_test_eax_jump_if_false (int label) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label); + + } else { + + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, " jz .L%d\n", label); + + } + +} + +static void emit_statement_test_pair_jump_if_false (const char *lo, const char *hi, int label) { + + int nonzero_label; + + if (!state->ofp) { + return; + } + + nonzero_label = anon_label++; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test %s, %s\n", hi, hi); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), nonzero_label); + fprintf (state->ofp, " test %s, %s\n", lo, lo); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), nonzero_label); + + } else { + + fprintf (state->ofp, " testl %%%s, %%%s\n", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", nonzero_label); + fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " jz .L%d\n", label); + fprintf (state->ofp, ".L%d:\n", nonzero_label); + + } + +} + +static int statement_condition_constant_known = 0; +static int statement_condition_constant_value = 0; +static int statement_ends_control_flow = 0; + +static void parse_statement (void); +static void parse_statement_suppressed (void); +static void emit_statement_jump_if_false (int label); + +static void replay_tmp_file_to_output (FILE *tmp, FILE *out) { + + int ch; + + if (!tmp || !out) { + return; + } + + fflush (tmp); + fseek (tmp, 0, SEEK_SET); + + while ((ch = fgetc (tmp)) != EOF) { + fputc (ch, out); + } + +} + +static void parse_for_header_expression_until (enum token_kind end_token) { + + char *name; + int global_index; + + const char *name_start, *name_caret; + unsigned long name_line; + + enum token_kind op; + struct local_symbol *lhs; + + int lhs_size; + int lhs_is_floating; + +#define FINISH_FOR_HEADER_EXPR(free_name) \ + do { \ + if (tok.kind == TOK_COMMA) { \ + get_token (); \ + if (free_name) { \ + free (name); \ + } \ + parse_for_header_expression_until (end_token); \ + return; \ + } \ + if (tok.kind != end_token) { \ + skip_balanced_until (end_token, TOK_EOF, TOK_EOF); \ + } \ + expect (end_token, end_token == TOK_SEMI ? ";" : ")"); \ + if (free_name) { \ + free (name); \ + } \ + return; \ + } while (0) + + if (tok.kind == end_token) { + return; + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + get_token (); + + if (tok.kind == TOK_IDENT) { + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + get_token (); + + lhs = find_local_symbol (name); + emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret); + free (name); + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--"); + } + + FINISH_FOR_HEADER_EXPR (0); + + } + + if (tok.kind != TOK_IDENT) { + + if (state->ofp) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + skip_balanced_until (end_token, TOK_EOF, TOK_EOF); + } + + FINISH_FOR_HEADER_EXPR (0); + + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret); + + FINISH_FOR_HEADER_EXPR (1); + + } + + if (tok.kind == TOK_LPAREN) { + + if (!find_local_symbol (name)) { + ensure_global_function_symbol (name, name_start, name_caret, name_line); + } + + emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line); + FINISH_FOR_HEADER_EXPR (1); + + } + + if (!is_assignment_operator (tok.kind)) { + + if (!find_local_symbol (name) && find_global_symbol (name) < 0) { + + int64_s ignored; + + if (!resolve_enum_constant (name, &ignored)) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + } + + } + + if (tok.kind == TOK_COMMA) { + + get_token (); + free (name); + + parse_for_header_expression_until (end_token); + return; + + } + + skip_balanced_until (end_token, TOK_EOF, TOK_EOF); + expect (end_token, end_token == TOK_SEMI ? ";" : ")"); + + free (name); + return; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + FINISH_FOR_HEADER_EXPR (1); + + } + + if (state->ofp) { + + lhs_size = lhs ? lhs->size : get_global_symbol_size (name); + lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name); + + if (lhs_is_floating) { + + if (!floating_assignment_operator_supported_now (op)) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator"); + skip_balanced_until (end_token, TOK_EOF, TOK_EOF); + + } else { + + if (op == TOK_ASSIGN) { + emit_load_floating_rhs_expression_now (lhs_size); + } else { + + emit_load_floating_symbol_now (lhs, name, lhs_size); + emit_load_floating_rhs_expression_now (lhs_size); + emit_floating_binary_now (op); + + } + + emit_store_floating_symbol_now (lhs, name, lhs_size); + + } + + } else if (lhs_size == (DATA_LLONG & 0x1f)) { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("eax", "edx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "eax", "edx"); + } + + } else { + emit_load_global64_to_pair ("eax", "edx", name); + } + + /* + * Compound assignments need the complete RHS expression. + * Using emit_load_assignment_rhs_to_pair() only consumes one + * primary operand, so e.g. + * + * final_value += symbol->frag->address + left_value; + * + * leaves the second + operand for the statement parser and + * reports "expected ;". Evaluate the full RHS in eax:edx, + * copy it to the RHS pair ebx:ecx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("eax"); + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + emit_mov_reg_to_reg_now ("ebx", "eax"); + emit_mov_reg_to_reg_now ("ecx", "edx"); + + emit_pop_reg_now ("edx"); + emit_pop_reg_now ("eax"); + + emit_preserve_assignment64_regs (op); + emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + emit_restore_assignment64_regs (op); + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_pair_to_global64 (lhs->static_label, "eax", "edx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "eax", "edx"); + } + + } else { + emit_store_pair_to_global64 (name, "eax", "edx"); + } + + } else { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("eax", lhs->static_label, lhs->size); + } else { + emit_load_local_to_reg ("eax", lhs->offset, lhs->size); + } + + } else { + emit_load_global_to_reg ("eax", name, lhs_size); + } + + emit_load_assignment_rhs_to_reg ("edx"); + emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_reg_to_global (lhs->static_label, lhs->size, "eax"); + } else { + emit_store_reg_to_local (lhs->offset, lhs->size, "eax"); + } + + } else { + emit_store_reg_to_global (name, lhs_size, "eax"); + } + + } + + } else { + skip_balanced_until (end_token, TOK_EOF, TOK_EOF); + } + + FINISH_FOR_HEADER_EXPR (1); + +#undef FINISH_FOR_HEADER_EXPR + +} + +static void parse_for_statement (void) { + + int loop_label = anon_label++; + int body_label = anon_label++; + int continue_label = anon_label++; + int break_label = anon_label++; + int old_break_label = current_break_label; + int old_continue_label = current_continue_label; + + long old_break_cleanup_base = current_break_cleanup_base; + long old_continue_cleanup_base = current_continue_cleanup_base; + + int cond_known = 0; + int cond_value = 1; + + FILE *saved_ofp; + FILE *step_tmp = 0; + + get_token (); + expect (TOK_LPAREN, "("); + + if (tok.kind == TOK_SEMI) { + get_token (); + } else { + parse_for_header_expression_until (TOK_SEMI); + } + + emit_statement_label (loop_label); + + if (tok.kind == TOK_SEMI) { + get_token (); + } else { + + emit_statement_jump_if_false (break_label); + + cond_known = statement_condition_constant_known; + cond_value = statement_condition_constant_value; + + if (tok.kind != TOK_SEMI) { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect (TOK_SEMI, ";"); + + } + + if (tok.kind == TOK_RPAREN) { + get_token (); + } else { + + if (state->ofp) { + step_tmp = tmpfile (); + } + + if (step_tmp) { + + saved_ofp = state->ofp; + + state->ofp = step_tmp; + parse_for_header_expression_until (TOK_RPAREN); + + state->ofp = saved_ofp; + + } else { + parse_for_header_expression_until (TOK_RPAREN); + } + + } + + current_break_label = break_label; + current_continue_label = continue_label; + current_break_cleanup_base = current_block_cleanup_bytes; + current_continue_cleanup_base = current_block_cleanup_bytes; + + if (cond_known && !cond_value) { + parse_statement_suppressed (); + } else { + + emit_statement_jump (body_label); + emit_statement_label (body_label); + + parse_statement (); + emit_statement_label (continue_label); + + if (step_tmp) { + + replay_tmp_file_to_output (step_tmp, state->ofp); + fclose (step_tmp); + + step_tmp = 0; + + } + + emit_statement_jump (loop_label); + + } + + if (step_tmp) { + fclose (step_tmp); + } + + emit_statement_label (break_label); + + current_break_label = old_break_label; + current_continue_label = old_continue_label; + current_break_cleanup_base = old_break_cleanup_base; + current_continue_cleanup_base = old_continue_cleanup_base; + + statement_ends_control_flow = 0; + +} + +static void parse_while_statement (void) { + + int loop_label = anon_label++; + int body_label = anon_label++; + int break_label = anon_label++; + int old_break_label = current_break_label; + int old_continue_label = current_continue_label; + + long old_break_cleanup_base = current_break_cleanup_base; + long old_continue_cleanup_base = current_continue_cleanup_base; + + int cond_known; + int cond_value; + + get_token (); + expect (TOK_LPAREN, "("); + + emit_statement_label (loop_label); + emit_statement_jump_if_false (break_label); + + cond_known = statement_condition_constant_known; + cond_value = statement_condition_constant_value; + + if (tok.kind != TOK_RPAREN) { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + expect (TOK_RPAREN, ")"); + + current_break_label = break_label; + current_continue_label = loop_label; + current_break_cleanup_base = current_block_cleanup_bytes; + current_continue_cleanup_base = current_block_cleanup_bytes; + + if (cond_known && !cond_value) { + parse_statement_suppressed (); + } else { + + emit_statement_jump (body_label); + emit_statement_label (body_label); + + parse_statement (); + + if (!statement_ends_control_flow) { + emit_statement_jump (loop_label); + } + + } + + emit_statement_label (break_label); + + current_break_label = old_break_label; + current_continue_label = old_continue_label; + current_break_cleanup_base = old_break_cleanup_base; + current_continue_cleanup_base = old_continue_cleanup_base; + + statement_ends_control_flow = 0; + +} + +static void parse_do_statement (void) { + + int body_label = anon_label++; + int cond_label = anon_label++; + int break_label = anon_label++; + int old_break_label = current_break_label; + int old_continue_label = current_continue_label; + + long old_break_cleanup_base = current_break_cleanup_base; + long old_continue_cleanup_base = current_continue_cleanup_base; + + int cond_known = 0; + int cond_value = 0; + + get_token (); + + current_break_label = break_label; + current_continue_label = cond_label; + current_break_cleanup_base = current_block_cleanup_bytes; + current_continue_cleanup_base = current_block_cleanup_bytes; + + emit_statement_jump (body_label); + + emit_statement_label (body_label); + parse_statement (); + + emit_statement_label (cond_label); + + if (_accept (TOK_WHILE)) { + + expect (TOK_LPAREN, "("); + emit_statement_jump_if_false (break_label); + + cond_known = statement_condition_constant_known; + cond_value = statement_condition_constant_value; + + if (tok.kind != TOK_RPAREN) { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + expect (TOK_RPAREN, ")"); + expect (TOK_SEMI, ";"); + + if (!cond_known || cond_value) { + emit_statement_jump (body_label); + } + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected while after do statement"); + } + + emit_statement_label (break_label); + + current_break_label = old_break_label; + current_continue_label = old_continue_label; + current_break_cleanup_base = old_break_cleanup_base; + current_continue_cleanup_base = old_continue_cleanup_base; + + statement_ends_control_flow = 0; + +} + +static int source_parenthesized_condition_has_logical_now (void) { + + const char *p = tok.caret; + int depth = 0; + + if (!p || *p != '(') { + return 0; + } + + for (; *p; p++) { + + if (*p == '(') { + + depth++; + continue; + + } + + if (*p == ')') { + + depth--; + + if (depth == 0) { + return 0; + } + + continue; + + } + + if (depth == 1 && ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|'))) { + return 1; + } + + if (*p == '"' || *p == '\'') { + + int quote = *p++; + + while (*p && *p != quote) { + + if (*p == '\\' && p[1]) { + p++; + } + + p++; + + } + + if (!*p) { + return 0; + } + + } + + } + + return 0; + +} + +static int statement_condition_emit_logical_tail (int label) { + + int skip_label; + + if (tok.kind == TOK_LOGAND) { + + get_token (); + + emit_statement_jump_if_false (label); + return 1; + + } + + if (tok.kind == TOK_LOGOR) { + + skip_label = anon_label++; + get_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), skip_label); + } else { + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, " jnz .L%d\n", skip_label); + } + + } + + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + + return 1; + + } + + return 0; + +} + +static int statement_condition_fold_logical_tail (int label) { + + enum token_kind logop; + + while (tok.kind == TOK_LOGOR || tok.kind == TOK_LOGAND) { + + logop = tok.kind; + + /* + * Constant short-circuit cases can discard the rest of the condition. + * + * true || anything -> true + * false && anything -> false + * + * Leave tok on ')' so the caller's expect(TOK_RPAREN) still works. + */ + if ((logop == TOK_LOGOR && statement_condition_constant_value) || (logop == TOK_LOGAND && !statement_condition_constant_value)) { + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + return 1; + + } + + /* + * The left side is non-decisive: + * + * false || rhs -> rhs + * true && rhs -> rhs + * + * So consume the logical operator and emit/fold the RHS normally. + */ + get_token (); + + statement_condition_constant_known = 0; + statement_condition_constant_value = 0; + + emit_statement_jump_if_false (label); + return 1; + + } + + return 0; + +} + +static int source_starts_with_parenthesized_assignment_at (const char *p) { + + int parens = 0; + + if (!p || *p != '(') { + return 0; + } + + while (*p == '(') { + + parens++; + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if (parens <= 0 || !((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + while (parens > 0 && *p == ')') { + + parens--; + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + /* + * A condition assignment normally looks like: + * + * if ((x = y)) + * + * At this point the scanner has consumed the opening paren(s) and the + * identifier. For the common form above, the assignment operator appears + * before the closing paren, so `parens' is still non-zero. The previous + * guard returned 0 before checking for '=', which made the statement + * condition parser treat the expression as just `(x' and then report a + * false "expected )" at the assignment operator. Check for assignment + * operators first, while still rejecting comparison operators. + */ + if (*p == '=') { + return p[1] != '='; + } + + if (parens != 0) { + return 0; + } + + if ((*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' || + *p == '&' || *p == '|' || *p == '^') && p[1] == '=') { + return 1; + } + + if ((*p == '<' && p[1] == '<' && p[2] == '=') || + (*p == '>' && p[1] == '>' && p[2] == '=')) { + return 1; + } + + return 0; + +} + +static int source_starts_with_parenthesized_assignment_now (void) { + + /** + * Only test the current token text. tok.start can point at the start of + * the whole source line, so scanning the whole line misclassifies a for + * condition such as: + * + * for (len = 0; (len < max_len) && str[len]; len++); + * + * as a parenthesized assignment because of the earlier ``(len = 0`` in the + * same line. This helper is a look-ahead guard; it must not match any + * parenthesized assignment except the one beginning at the current token. + */ + if (source_starts_with_parenthesized_assignment_at (tok.caret)) { + return 1; + } + + return 0; + +} + +static int parenthesized_assignment_open_parens = 0; +static int parenthesized_assignment_closed_parens = 0; + +static void consume_parenthesized_assignment_remaining_closes (void) { + + while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) { + + get_token (); + parenthesized_assignment_closed_parens++; + + } + +} + +static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned) { + + char *name; + int global_index; + + const char *name_start, *name_caret; + unsigned long name_line; + + enum token_kind op; + struct local_symbol *lhs; + + int lhs_size; + int lhs_is_floating; + int lhs_is_unsigned; + int paren_count = 0; + + parenthesized_assignment_open_parens = 0; + parenthesized_assignment_closed_parens = 0; + + if (out_is_unsigned) { + *out_is_unsigned = 0; + } + + if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_assignment_now ()) { + return 0; + } + + while (tok.kind == TOK_LPAREN) { + + paren_count++; + get_token (); + + } + + if (tok.kind != TOK_IDENT) { + return 0; + } + + parenthesized_assignment_open_parens = paren_count; + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) { + + get_token (); + parenthesized_assignment_closed_parens++; + + } + + if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) { + + enum token_kind member_op = tok.kind; + char *member; + + const char *member_start; + const char *member_caret; + + unsigned long member_line; + + int member_offset = 0; + int member_size = DATA_INT & 0x1f; + + get_token (); + + member_start = tok.start; + member_caret = tok.caret; + member_line = get_line_number (); + + if (tok.kind != TOK_IDENT) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : "."); + + free (name); + return 1; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info (member, &member_offset, &member_size)) { + + report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member); + + free (member); + free (name); + + return 1; + + } + + free (member); + + if (!is_assignment_operator (tok.kind)) { + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + expect (TOK_SEMI, ";"); + + free (name); + return 1; + + } + + if (state->ofp) { + + if (member_op == TOK_ARROW) { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("edx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("edx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("edx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("edx", name); + } + + } + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("edx"); + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size); + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + + if (tok.kind == TOK_TILDE) { + + int64_s rhs_const; + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + rhs_const = const64_from_current_foldable_expr (); + expect (TOK_RPAREN, ")"); + + } else { + rhs_const = const64_from_current_foldable_expr (); + } + + rhs_const.low = (~rhs_const.low) & U32_MASK; + rhs_const.high = (~rhs_const.high) & U32_MASK; + + emit_load_const32_to_reg_now ("edx", rhs_const); + + } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) { + + int64_s rhs_const = const64_from_current_foldable_expr (); + emit_load_const32_to_reg_now ("edx", rhs_const); + + } else { + emit_load_assignment_rhs_expression_to_reg ("edx"); + } + + emit_pop_reg_now ("eax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size); + + } else { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (!is_assignment_operator (tok.kind)) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + lhs = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!lhs && global_index < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name); + + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + expect (TOK_RPAREN, ")"); + + free (name); + return 1; + + } + + lhs_size = lhs ? lhs->size : get_global_symbol_size (name); + lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name); + lhs_is_unsigned = lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name); + + if (out_is_unsigned) { + *out_is_unsigned = lhs_is_unsigned; + } + + if (state->ofp) { + + if (lhs_is_floating) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression in condition not implemented"); + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + + } else if (lhs_size == (DATA_LLONG & 0x1f)) { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("eax", "edx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "eax", "edx"); + } + + } else { + emit_load_global64_to_pair ("eax", "edx", name); + } + + /* + * Compound assignments need the complete RHS expression. + * Using emit_load_assignment_rhs_to_pair() only consumes one + * primary operand, so e.g. + * + * final_value += symbol->frag->address + left_value; + * + * leaves the second + operand for the statement parser and + * reports "expected ;". Evaluate the full RHS in eax:edx, + * copy it to the RHS pair ebx:ecx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("eax"); + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned); + + emit_mov_reg_to_reg_now ("ebx", "eax"); + emit_mov_reg_to_reg_now ("ecx", "edx"); + + emit_pop_reg_now ("edx"); + emit_pop_reg_now ("eax"); + + emit_preserve_assignment64_regs (op); + emit_assignment_binary_op64 (op, lhs_is_unsigned); + emit_restore_assignment64_regs (op); + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_pair_to_global64 (lhs->static_label, "eax", "edx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "eax", "edx"); + } + + } else { + emit_store_pair_to_global64 (name, "eax", "edx"); + } + + } else { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg (reg, lhs->static_label, lhs->size); + } else { + emit_load_local_to_reg (reg, lhs->offset, lhs->size); + } + + } else { + emit_load_global_to_reg (reg, name, lhs_size); + } + + emit_load_assignment_rhs_to_reg ("edx"); + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov eax, %s\n", reg); + } else { + fprintf (state->ofp, " movl %%%s, %%eax\n", reg); + } + + } + + emit_assignment_binary_op (op, lhs_is_unsigned); + + if (strcmp (reg, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, eax\n", reg); + } else { + fprintf (state->ofp, " movl %%eax, %%%s\n", reg); + } + + } + + } + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_store_reg_to_global (lhs->static_label, lhs->size, reg); + } else { + emit_store_reg_to_local (lhs->offset, lhs->size, reg); + } + + } else { + emit_store_reg_to_global (name, lhs_size, reg); + } + + } + + } else { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + consume_parenthesized_assignment_remaining_closes (); + + free (name); + return 1; + +} + +static void emit_statement_jump_if_false (int label) { + + enum token_kind op; + int is_unsigned; + + flush_pending_statement_labels (); + + statement_condition_constant_known = 0; + statement_condition_constant_value = 0; + + if (tok.kind == TOK_LPAREN && source_parenthesized_condition_has_logical_now ()) { + + /* + * Parse the whole parenthesized logical expression as an expression. + * The older recursive condition parser stopped after the first operand + * of nested logical groups such as: + * + * a && (b || c || d) + * + * and then the outer caller immediately expected the closing ')', + * leaving the inner '||' tokens unconsumed and reporting a false + * "expected )" at the first member expression. + */ + get_token (); + + emit_load_assignment_rhs_expression_to_reg ("eax"); + expect (TOK_RPAREN, ")"); + + if (tok.kind == TOK_LOGOR) { + + int skip_label = anon_label++; + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test eax, eax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), skip_label); + + } else { + + fprintf (state->ofp, " testl %%eax, %%eax\n"); + fprintf (state->ofp, " jnz .L%d\n", skip_label); + + } + + } + + get_token (); + + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + + return; + + } + + emit_statement_test_eax_jump_if_false (label); + statement_condition_emit_logical_tail (label); + + return; + + } + + if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &is_unsigned)) { + + while (is_arithmetic_binary_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_rhs_to_reg ("edx"); + emit_assignment_binary_op (op, is_unsigned); + + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_rhs_expression_to_reg ("edx"); + consume_parenthesized_assignment_remaining_closes (); + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + + return; + + } + + consume_parenthesized_assignment_remaining_closes (); + emit_statement_test_eax_jump_if_false (label); + return; + + } + + if (statement_condition_starts_with_ident_call_now ()) { + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + statement_condition_emit_logical_tail (label); + + return; + + } + + emit_statement_test_eax_jump_if_false (label); + statement_condition_emit_logical_tail (label); + + return; + + } + + /* + * Fold simple constant conditions at parse time. This avoids emitting: + * + * mov eax, 1 + * test eax, eax + * jz ... + * + * for things like if (1), and avoids cmp/jcc for things like + * if (1 < 10), if (5 > 2), if (1ULL < 10LL), etc. + */ + if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) { + + long double left_float = 0.0L; + + int left_float_constant = 0; + int float_size = DATA_DOUBLE & 0x1f; + + if (token_is_const_floating_condition_operand_now ()) { + + left_float = parse_floating_const_expr_value_now (); + left_float_constant = 1; + + } else { + emit_load_floating_rhs_expression_now (float_size); + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (left_float_constant && (token_is_floating_constant_now () || token_is_const_condition_operand_now ())) { + + long double right_float; + + if (token_is_floating_constant_now ()) { + right_float = parse_floating_const_expr_value_now (); + } else { + + int64_s right_int = const64_from_current_operand (); + + right_float = (long double) right_int.low; + + if (right_int.high != 0) { + right_float += ((long double) right_int.high) * 4294967296.0L; + } + + } + + statement_condition_constant_known = 1; + statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + if (left_float_constant) { + emit_load_floating_ld_now (float_size, left_float); + } + + emit_load_floating_rhs_expression_now (float_size); + emit_statement_floating_compare_jump_if_false (op, label); + + return; + + } + + if (left_float_constant) { + + statement_condition_constant_known = 1; + statement_condition_constant_value = left_float != 0.0L ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + emit_load_floating_ld_now (float_size, 0.0L); + emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); + + return; + + } + + if (token_is_const_condition_operand_now () || const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start)) { + + int fold_whole_expr = const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start); + int left_unsigned = rhs_current_operand_is_unsigned_now (); + + int64_s left; + + if (fold_whole_expr) { + left = const64_from_current_foldable_expr (); + } else { + left = const64_from_current_operand (); + } + + if (is_arithmetic_binary_operator (tok.kind)) { + + emit_statement_const32_to_eax (left); + + while (is_arithmetic_binary_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + left_unsigned = 1; + } + + emit_load_assignment_rhs_to_reg ("edx"); + emit_assignment_binary_op (op, left_unsigned); + + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + is_unsigned = left_unsigned; + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + + return; + + } + + emit_statement_test_eax_jump_if_false (label); + return; + + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + is_unsigned = left_unsigned; + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (token_is_floating_constant_now ()) { + + long double left_float; + long double right_float; + + left_float = (long double) left.low; + + if (left.high != 0) { + left_float += ((long double) left.high) * 4294967296.0L; + } + + right_float = parse_floating_const_expr_value_now (); + + statement_condition_constant_known = 1; + statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + if (token_is_const_condition_operand_now ()) { + + int64_s right; + + if (const_integer_expr_text_is_foldable_now (tok.start)) { + right = const64_from_current_foldable_expr (); + } else { + right = const64_from_current_operand (); + } + + statement_condition_constant_known = 1; + statement_condition_constant_value = statement_compare_const64_true (left, op, right, is_unsigned) ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + if (rhs_current_operand_is_floating_now ()) { + + long double left_float; + + left_float = (long double) left.low; + + if (left.high != 0) { + left_float += ((long double) left.high) * 4294967296.0L; + } + + emit_load_floating_ld_now (DATA_DOUBLE & 0x1f, left_float); + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + emit_statement_floating_compare_jump_if_false (op, label); + + return; + + } + + emit_statement_const32_to_eax (left); + emit_load_assignment_rhs_expression_to_reg ("edx"); + + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + statement_condition_emit_logical_tail (label); + + return; + + } + + statement_condition_constant_known = 1; + statement_condition_constant_value = int64_statement_truth_value (left) ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + /* + * A leading unary ! always yields an int truth value. Do not route it + * through the 64-bit condition path just because the operand text happens + * to mention a symbol that the coarse scanner thinks is 64-bit; that path + * expects an unconverted 64-bit value and will compare EDX:EAX against + * uninitialised ECX:EBX for a plain if (!p). + */ + if (tok.kind == TOK_XMARK) { + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_statement_test_eax_jump_if_false (label); + + statement_condition_emit_logical_tail (label); + return; + + } + + if (current_expression_mentions_64bit_symbol_now ()) { + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " push ebx\n"); + fprintf (state->ofp, " push eax\n"); + fprintf (state->ofp, " push edx\n"); + + } else { + + fprintf (state->ofp, " pushl %%ebx\n"); + fprintf (state->ofp, " pushl %%eax\n"); + fprintf (state->ofp, " pushl %%edx\n"); + + } + + } + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov ebx, eax\n"); + fprintf (state->ofp, " mov ecx, edx\n"); + fprintf (state->ofp, " pop edx\n"); + fprintf (state->ofp, " pop eax\n"); + + } else { + + fprintf (state->ofp, " movl %%eax, %%ebx\n"); + fprintf (state->ofp, " movl %%edx, %%ecx\n"); + fprintf (state->ofp, " popl %%edx\n"); + fprintf (state->ofp, " popl %%eax\n"); + + } + + } + + emit_statement_cmp64_to_eax (op, is_unsigned); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " pop ebx\n"); + } else { + fprintf (state->ofp, " popl %%ebx\n"); + } + + } + + emit_statement_test_eax_jump_if_false (label); + return; + + } + + /* + * No explicit comparison follows, so this is just the truth value of + * the 64-bit expression in EDX:EAX. Do not route it through the + * generic 64-bit comparison helper as TOK_NOTEQ: that helper compares + * EDX:EAX against ECX:EBX, and there is no RHS pair here. This broke + * conditions such as: + * + * if ((entry_point = coff_calculate_entry_point ())) return; + * + * after the assignment stored the result and left ECX holding the + * destination address instead of zero. + */ + emit_statement_test_pair_jump_if_false ("eax", "edx", label); + statement_condition_emit_logical_tail (label); + + return; + + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + statement_condition_emit_logical_tail (label); + + return; + + } + + emit_statement_test_eax_jump_if_false (label); + statement_condition_emit_logical_tail (label); + +} + +static int token_text_starts_void_cast_now (void) { + + const char *p = tok.caret; + + if (!p || *p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "void", 4) != 0) { + return 0; + } + + p += 4; + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + return *p == ')'; + +} + +static int parse_void_cast_discard_statement (void) { + + if (tok.kind != TOK_LPAREN || !token_text_starts_void_cast_now ()) { + return 0; + } + + get_token (); + + expect (TOK_VOID, "void"); + expect (TOK_RPAREN, ")"); + + if (tok.kind != TOK_SEMI) { + + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (tok.kind != TOK_SEMI) { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + } + + expect (TOK_SEMI, ";"); + return 1; + +} + +static int token_text_looks_like_postfix_assignment_now (void) { + + const char *p; + int saw_postfix; + int paren_depth; + int bracket_depth; + + if (tok.caret) { + p = tok.caret; + } else if (tok.start) { + p = tok.start; + } else { + return 0; + } + + saw_postfix = 0; + paren_depth = 0; + bracket_depth = 0; + + while (*p && *p != ';' && *p != '\n') { + + if (*p == '(') { + + /** + * A postfix/member expression followed by a top-level '(' is a + * function call through that member, not a postfix assignment. + * This pre-scan must reject it before the destructive postfix + * assignment parser consumes the tokens. + */ + if (saw_postfix && paren_depth == 0 && bracket_depth == 0) { + return 0; + } + + paren_depth++; + + } else if (*p == ')') { + if (paren_depth > 0) paren_depth--; + } else if (*p == '[') { + + saw_postfix = 1; + bracket_depth++; + + } else if (*p == ']') { + if (bracket_depth > 0) bracket_depth--; + } else if (paren_depth == 0 && bracket_depth == 0 && *p == ',') { + return 0; + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') { + saw_postfix = 1; + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') { + + saw_postfix = 1; + p++; + + } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') { + + if (p[1] == '=') { + return 0; + } + + return saw_postfix; + + } + + p++; + + } + + return 0; + +} + +static int source_starts_with_parenthesized_indirect_call_now (void) { + + const char *p = tok.caret; + + int member_depth = 1; + int saw_member = 0; + int depth; + + if (!p || *p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '*') { + return 0; + } + + depth = 1; + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + /* + * Accept both + * + * (*obj->member)(...) + * (*(obj->member))(...) + * + * but keep this recognizer deliberately narrow. It must not claim an + * arbitrary parenthesized expression, otherwise normal statement parsing + * and C labels can be pulled into this special call path. + */ + if (*p == '(') { + + depth++; + + member_depth = 2; + p++; + + } + + while (*p && depth > 0) { + + if (*p == '(') { + depth++; + } else if (*p == ')') { + + depth--; + + if (depth == 0) { + break; + } + + } else if (depth == member_depth && *p == '-' && p[1] == '>') { + + saw_member = 1; + p++; + + } else if (depth == member_depth && *p == '.') { + saw_member = 1; + } + + p++; + + } + + if (depth != 0 || !saw_member) { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '('; + +} + +static int parse_postfix_assignment_statement_now (void) { + + char *name; + + struct local_symbol *src; + int global_index; + + enum token_kind assign_op; + int lvalue_size; + + if (tok.kind != TOK_IDENT || !token_text_looks_like_postfix_assignment_now ()) { + return 0; + } + + name = xstrdup (tok.ident); + + { + + const char *name_start = tok.start; + const char *name_caret = tok.caret; + + unsigned long name_line = get_line_number (); + get_token (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!src && global_index < 0) { + + free (name); + return 0; + + } + + if (!emit_parse_postfix_copy_source_address_now ("edx", src, name, name_start, name_caret, name_line)) { + + free (name); + return 0; + + } + + } + + free (name); + + if (!is_assignment_operator (tok.kind)) { + return 0; + } + + assign_op = tok.kind; + lvalue_size = index_step_size (postfix_copy_lvalue_size); + + if (lvalue_size <= 0) { + lvalue_size = DATA_INT & 0x1f; + } + + get_token (); + + if (assign_op == TOK_ASSIGN) { + + if (token_is_floating_constant_now ()) { + + emit_push_reg_now ("edx"); + emit_load_floating_rhs_expression_now (lvalue_size); + + emit_pop_reg_now ("edx"); + emit_store_floating_member_to_addr_reg_now ("edx", 0, lvalue_size); + + return 1; + + } + + if (lvalue_size == (DATA_LLONG & 0x1f)) { + + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1); + emit_pop_reg_now ("ecx"); + + emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx"); + return 1; + + } + + if (lvalue_size > (DATA_LLONG & 0x1f) + && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now () + && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION + && get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) { + + char *rhs_name = xstrdup (tok.ident); + const char *rhs_start = tok.start; + const char *rhs_caret = tok.caret; + unsigned long rhs_line = get_line_number (); + + emit_push_reg_now ("edx"); + + pending_struct_return_lhs = 0; + pending_struct_return_global_name = 0; + pending_struct_return_stack_address = 1; + pending_struct_return_stack_offset = 0; + + get_token (); + emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("edx"); + + free (rhs_name); + return 1; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, lvalue_size)) { + return 1; + } + + /** + * Keep the computed lvalue address across the RHS evaluation. + * The RHS emitter is free to use edx for indexing, calls, and + * arithmetic, so storing through edx after it can corrupt memory. + */ + emit_push_reg_now ("edx"); + + emit_load_assignment_rhs_expression_to_reg ("eax"); + emit_pop_reg_now ("edx"); + + } else { + + emit_push_reg_now ("edx"); + + emit_load_member_from_addr_reg_now ("eax", "edx", 0, lvalue_size); + emit_push_reg_now ("eax"); + + emit_load_assignment_rhs_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + emit_assignment_binary_op (assign_op, 0); + emit_pop_reg_now ("edx"); + + } + + emit_store_reg_to_deref_reg_now ("edx", "eax", lvalue_size); + return 1; + +} + +static int parse_parenthesized_member_function_pointer_call_statement (void) { + + struct local_symbol *src; + char *name; + + int global_index; + int wrapped_member_expr = 0; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + int base_size = DATA_INT & 0x1f; + + const char *base_tag_name = 0; + + if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_indirect_call_now ()) { + return 0; + } + + get_token (); + + if (tok.kind != TOK_STAR) { + return 0; + } + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + wrapped_member_expr = 1; + get_token (); + + } + + if (tok.kind != TOK_IDENT || !tok.ident) { + return 0; + } + + name = xstrdup (tok.ident); + name_start = tok.start; + name_caret = tok.caret; + name_line = get_line_number (); + + get_token (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!src && global_index < 0) { + + free (name); + return 0; + + } + + if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) { + + if (src) { + + base_size = src->pointer_depth > 0 ? src->pointed_size : src->size; + base_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : 0; + + } else { + + base_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name); + base_tag_name = 0; + + } + + } else if (tok.kind == TOK_DOT) { + + if (src) { + + base_size = src->size; + base_tag_name = 0; + + } else { + + base_size = get_global_symbol_size (name); + base_tag_name = 0; + + } + + } else { + + free (name); + return 0; + + } + + postfix_copy_lvalue_size = base_size; + postfix_copy_lvalue_tag_name = base_tag_name; + + if (!emit_parse_postfix_copy_source_address_now ("ecx", src, name, name_start, name_caret, name_line)) { + + free (name); + return 0; + + } + + free (name); + + if (wrapped_member_expr) { + expect (TOK_RPAREN, ")"); + } + + expect (TOK_RPAREN, ")"); + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f); + emit_call_pointer_in_reg_now ("ecx", "eax"); + + expect_semi_or_recover (); + return 1; + +} + +static void parse_statement_suppressed (void) { + + FILE *save_ofp = state->ofp; + + state->ofp = 0; + parse_statement (); + state->ofp = save_ofp; + +} + +static void parse_statement (void) { + + statement_ends_control_flow = 0; + flush_pending_statement_labels (); + + if (parse_inline_asm_statement ()) { + return; + } + + if (parse_prefix_incdec_statement ()) { + return; + } + + if (parse_void_cast_discard_statement ()) { + return; + } + + + if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) { + + get_token (); + + if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now ("eax")) { + + expect_semi_or_recover (); + return; + + } + + } + + if (parse_parenthesized_deref_subscript_statement ()) { + + expect_semi_or_recover (); + return; + + } + + if (parse_parenthesized_member_function_pointer_call_statement ()) { + return; + } + + if (tok.kind == TOK_LPAREN) { + + int parenthesized_assignment_is_unsigned; + + if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &parenthesized_assignment_is_unsigned)) { + + expect_semi_or_recover (); + return; + + } + + } + + if (tok.kind == TOK_LPAREN && parse_parenthesized_indirect_member_assignment_statement ()) { + return; + } + + if (parse_indirect_assignment_statement ()) { + return; + } + + if (parse_postfix_assignment_statement_now ()) { + + expect_semi_or_recover (); + return; + + } + + if (parse_identifier_assignment_statement ()) { + return; + } + + if (tok.kind == TOK_RETURN) { + + int has_value = 0; + long v = 0; + + const char *ret_start = tok.start; + const char *ret_caret = tok.caret; + + int ret_line = get_line_number (); + + current_function_has_return_statement = 1; + get_token (); + + if (tok.kind != TOK_SEMI) { + + if (current_function_is_void) { + + FILE *saved_return_ofp = state->ofp; + state->ofp = 0; + + emit_load_assignment_rhs_expression_to_reg ("eax"); + state->ofp = saved_return_ofp; + + } else if (current_function_is_floating) { + + if (state->ofp && pending_return_jump) { + emit_pending_return_jump (); + } + + emit_load_floating_rhs_expression_now (current_function_return_size); + + } else { + + if (state->ofp && pending_return_jump) { + emit_pending_return_jump (); + } + + if (state->ofp) { + + if (current_function_returns_aggregate) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov edx, dword [ebp + 8]\n" : " mov edx, dword ptr [ebp + 8]\n")); + } else { + fprintf (state->ofp, " movl 8(%%ebp), %%edx\n"); + } + + if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, current_function_return_size)) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } + + } else if (current_function_return_size == (DATA_LLONG & 0x1f)) { + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned); + } else if (current_expression_mentions_64bit_symbol_now ()) { + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned); + } else { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } + + } else { + v = const_from_current_expr (); + } + + } + + has_value = 1; + + } + + if (current_function_is_void && has_value) { + report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with a value in void function"); + } else if (!current_function_is_void && !has_value) { + report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with no value in non-void function"); + } + + if (state->ofp) { + + if (pending_return_jump) { + emit_pending_return_jump (); + } + + if (!current_function_is_floating && !has_value) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov eax, %ld\n", v); + + if (current_function_return_size == (DATA_LLONG & 0x1f)) { + fprintf (state->ofp, " cdq\n"); + } + + } else { + + fprintf (state->ofp, " movl $%ld, %%eax\n", v); + + if (current_function_return_size == (DATA_LLONG & 0x1f)) { + fprintf (state->ofp, " cdq\n"); + } + + } + + } + + pending_return_jump = 1; + statement_ends_control_flow = 1; + + } + + expect (TOK_SEMI, ";"); + return; + + } + + if (tok.kind == TOK_LBRACE) { + + parse_block (); + return; + + } + + if (tok.kind == TOK_IF) { + + int else_label = anon_label++; + int end_label = anon_label++; + + get_token (); + expect (TOK_LPAREN, "("); + + /* + * Compile the full if-condition as an expression before testing it. + * The older specialised condition emitter jumps to the false label as + * soon as the left side of a logical OR is false, which skips the RHS. + * That miscompiled expressions such as: + * + * if ((base && !base->dword) || (index && ...)) + * + * and made pdas reject valid 32-bit AT&T base/index operands. + */ + emit_statement_jump_if_false (else_label); + + if (tok.kind != TOK_RPAREN) { + skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF); + } + + expect (TOK_RPAREN, ")"); + + if (statement_condition_constant_known) { + + if (statement_condition_constant_value) { + + parse_statement (); + + if (_accept (TOK_ELSE)) { + parse_statement_suppressed (); + } + + } else { + + parse_statement_suppressed (); + + if (_accept (TOK_ELSE)) { + parse_statement (); + } + + } + + return; + + } + + parse_statement (); + + { + + int then_ends_control_flow = statement_ends_control_flow; + + if (_accept (TOK_ELSE)) { + + if (pending_return_jump) { + + emit_pending_return_jump (); + then_ends_control_flow = 1; + + } else if (!then_ends_control_flow) { + emit_statement_jump (end_label); + } + + emit_statement_label (else_label); + parse_statement (); + + if (pending_return_jump) { + + emit_pending_return_jump (); + statement_ends_control_flow = 1; + + } + + emit_statement_label (end_label); + statement_ends_control_flow = then_ends_control_flow && statement_ends_control_flow; + + } else { + + if (pending_return_jump) { + emit_pending_return_jump (); + } + + emit_statement_label (else_label); + statement_ends_control_flow = 0; + + } + + } + + return; + + } + + if (tok.kind == TOK_SWITCH) { + + parse_switch_statement (); + return; + + } + + if (tok.kind == TOK_WHILE) { + + parse_while_statement (); + return; + + } + + if (tok.kind == TOK_DO) { + + parse_do_statement (); + return; + + } + + if (tok.kind == TOK_FOR) { + + parse_for_statement (); + return; + + } + + if (tok.kind == TOK_GOTO) { + + const char *label_start; + const char *label_caret; + + unsigned long label_line; + char *label_name = 0; + + get_token (); + + label_start = tok.start; + label_caret = tok.caret; + label_line = get_line_number (); + + if (tok.kind == TOK_IDENT) { + + label_name = xstrdup (tok.ident); + get_token (); + + /* + * Do not eagerly pop block-local temporary storage for a forward + * goto. A label can legally be later in the same block after + * nested statements, and the earlier text scan was too + * conservative: it treated the first nested '}' as the end of the + * current block. That produced an extra add to esp before + * `goto skip;` in i386_gen.c's process_bitfield_init(), corrupting + * the stack before the local label was reached. + */ + + reference_goto_label (label_name, label_line, label_start, label_caret); + statement_ends_control_flow = 1; + + free (label_name); + + } else { + report_line_at (get_filename (), label_line, REPORT_ERROR, label_start, label_caret, "expected label name after goto"); + } + + expect (TOK_SEMI, ";"); + return; + + } + + if (tok.kind == TOK_BREAK || tok.kind == TOK_CONTINUE) { + + enum token_kind jump_kind = tok.kind; + int target_label = -1; + + get_token (); + + if (jump_kind == TOK_BREAK) { + target_label = current_break_label; + } else { + target_label = current_continue_label; + } + + if (target_label >= 0) { + + { + + long cleanup_base = (jump_kind == TOK_BREAK) ? current_break_cleanup_base : current_continue_cleanup_base; + long cleanup_bytes = current_block_cleanup_bytes - cleanup_base; + + if (cleanup_bytes > 0) { + emit_stack_adjust (cleanup_bytes, 0); + } + + } + + emit_statement_jump (target_label); + statement_ends_control_flow = 1; + + } + + if (tok.kind != TOK_SEMI) { + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + } + + expect (TOK_SEMI, ";"); + return; + + } + + if (tok.kind == TOK_CASE) { + + long value; + unsigned long case_line = get_line_number (); + + const char *case_start = tok.start; + const char *case_caret = tok.caret; + + get_token (); + + value = const_from_current_case_expr (); + add_switch_case_label (value, case_line, case_start, case_caret); + + if (tok.kind != TOK_COLON) { + skip_balanced_until (TOK_COLON, TOK_EOF, TOK_EOF); + } + + expect (TOK_COLON, ":"); + statement_ends_control_flow = 0; + + return; + + } + + if (tok.kind == TOK_DEFAULT) { + + unsigned long default_line = get_line_number (); + + const char *default_start = tok.start; + const char *default_caret = tok.caret; + + get_token (); + + set_switch_default_label (default_line, default_start, default_caret); + expect (TOK_COLON, ":"); + + statement_ends_control_flow = 0; + return; + + } + + if (tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) { + + if (parse_postfix_assignment_statement_now ()) { + + if (tok.kind == TOK_SEMI) { + + get_token (); + return; + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect (TOK_SEMI, ";"); + return; + + } + + emit_load_assignment_rhs_expression_to_reg ("eax"); + + if (tok.kind == TOK_SEMI) { + + get_token (); + return; + + } + + skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF); + + expect (TOK_SEMI, ";"); + return; + + } + + expect (TOK_SEMI, ";"); + +} + +static void parse_function_body (const char *name, int storage_class, int is_inline, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, unsigned long function_line, const char *function_start, const char *function_caret) { + + int old_return_label = current_return_label; + int should_emit = 1; + int emit_body = 1; + int emit_public = 1; + int emit_inline_definition_to_output = 1; + + FILE *saved_ofp; + FILE *inline_tmp = 0; + + char *inline_asm_text = 0; + int capture_inline_body = 0; + + int saved_declarator_function_param_count; + int saved_declarator_function_has_prototype; + int saved_declarator_function_is_variadic; + int saved_current_section = current_section; + + int old_function_is_void; + int old_function_is_floating; + int old_function_return_size; + int old_function_return_is_unsigned; + int old_function_returns_aggregate; + int old_function_has_return_statement; + + char *function_filename_copy = 0; + + /** + * Inline definitions are compiled into a temporary assembler buffer so + * calls in this translation unit can be expanded at the call site. + * + * inline int f(...) { ... } + * static inline int f(...) { ... } + * Remember the generated body for inline expansion. Do not emit an + * out-of-line copy unless a later rule explicitly asks for one. + * + * extern inline int f(...) { ... } + * Remember the generated body and also emit the normal public + * definition. + * + * int f(...) { ... } + * Normal external definition. Emit it as public. + */ + if (storage_class == STORAGE_STATIC) { + emit_public = 0; + } else if (is_inline && storage_class != STORAGE_EXTERN) { + emit_public = 0; + } + + if (is_inline && storage_class != STORAGE_EXTERN && !return_is_floating) { + emit_inline_definition_to_output = 0; + } + + saved_ofp = state->ofp; + + saved_declarator_function_param_count = declarator_function_param_count; + saved_declarator_function_has_prototype = declarator_function_has_prototype; + saved_declarator_function_is_variadic = declarator_function_is_variadic; + + if (get_filename ()) { + function_filename_copy = xstrdup (get_filename ()); + } + + capture_inline_body = is_inline && saved_ofp != 0; + + if (capture_inline_body) { + + inline_tmp = tmpfile (); + + if (!inline_tmp) { + capture_inline_body = 0; + } + + } + + if (emit_body) { + + should_emit = add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 0, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line); + + set_global_symbol_size (name, return_is_void ? DATA_VOID : return_size); + set_global_symbol_unsigned (name, 0); + set_global_symbol_floating (name, return_is_floating); + set_global_symbol_returns_void (name, return_is_void); + set_global_symbol_param_count (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, saved_declarator_function_is_variadic); + + if (is_inline) { + + remember_inline_function_signature (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, + return_is_void, return_is_floating, return_size); + + } + + } + + old_function_has_return_statement = current_function_has_return_statement; + old_function_is_void = current_function_is_void; + old_function_is_floating = current_function_is_floating; + old_function_return_size = current_function_return_size; + old_function_return_is_unsigned = current_function_return_is_unsigned; + old_function_returns_aggregate = current_function_returns_aggregate; + + current_function_is_void = return_is_void; + current_function_is_floating = return_is_floating; + current_function_return_is_unsigned = return_is_unsigned; + current_function_return_size = return_size; + current_function_returns_aggregate = (!return_is_void && !return_is_floating && return_size > (DATA_LLONG & 0x1f)); + current_function_has_return_statement = 0; + + reset_local_symbols (); + reset_goto_labels (); + + install_pending_params_as_locals (); + clear_pending_params (); + + if (!emit_body || !should_emit) { + state->ofp = 0; + } else if (capture_inline_body) { + state->ofp = inline_tmp; + } + + current_return_label = anon_label++; + pending_return_jump = 0; + + emit_function_start (name, !emit_public); + + parse_old_style_param_decls (); + parse_block (); + check_goto_labels (); + + if (!current_function_is_void && !current_function_has_return_statement) { + report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "control reaches end of non-void function"); + } + + pending_return_jump = 0; + + emit_function_end (); + emit_goto_trampolines (); + + if (capture_inline_body && inline_tmp) { + + inline_asm_text = read_tmp_file_text (inline_tmp); + + if (inline_asm_text) { + + if (emit_inline_definition_to_output) { + fputs (inline_asm_text, saved_ofp); + } + + remember_inline_function (name, inline_asm_text, current_return_label, + saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, + return_is_void, return_is_floating, return_size); + + } + + fclose (inline_tmp); + inline_tmp = 0; + + } + + reset_local_symbols (); + reset_goto_labels (); + + current_function_has_return_statement = old_function_has_return_statement; + current_function_is_void = old_function_is_void; + current_function_is_floating = old_function_is_floating; + current_function_return_size = old_function_return_size; + current_function_return_is_unsigned = old_function_return_is_unsigned; + current_function_returns_aggregate = old_function_returns_aggregate; + + current_return_label = old_return_label; + state->ofp = saved_ofp; + + if (capture_inline_body) { + current_section = saved_current_section; + } + + if (function_filename_copy) { + free (function_filename_copy); + } + + if (inline_asm_text) { + free (inline_asm_text); + } + +} + +static int parse_possible_knr_function (void) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + + if (!token_is_ident ()) { + return 0; + } + + name_line = get_line_number (); + + name_start = tok.start; + name_caret = tok.caret; + + name = take_ident (); + + if (!_accept (TOK_LPAREN)) { + + free (name); + return 0; + + } + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + if (token_is_ident ()) { + + add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0); + get_token (); + + } else { + skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + if (is_type_start (tok.kind) || tok.kind == TOK_LBRACE) { + + parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, DATA_INT & 0x1f, name_line, name_start, name_caret); + + free (name); + return 1; + + } + + free (name); + return 0; + +} + +static int is_string_token (void) { + return tok.kind == TOK_PPSTR || tok.kind == TOK_LSTR; +} + +static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) { + + if (*count < max_values) { + zext64 (&values[*count], value & 0xffU); + } + + (*count)++; + +} + +static int parse_octal_escape_value (const char **ps) { + + const char *s = *ps; + int value = 0, i; + + for (i = 0; i < 3; i++) { + + if (*s < '0' || *s > '7') { + break; + } + + value = value * 8 + (*s - '0'); + s++; + + } + + *ps = s; + return value; + +} + +static int parse_hex_escape_value (const char **ps) { + + const char *s = *ps; + + int value = 0; + int any = 0; + + while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) { + + int digit; + + if (*s >= '0' && *s <= '9') { + digit = *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + digit = *s - 'a' + 10; + } else { + digit = *s - 'A' + 10; + } + + value = (value << 4) + digit; + any = 1; + + s++; + + } + + if (!any) { + value = 'x'; + } + + *ps = s; + return value; + +} + +static void parse_string_initializer_values (int64_s *values, int max_values, int *count) { + + while (is_string_token ()) { + + const char *s; + int quote; + + s = tok.ident; + + if (tok.kind == TOK_LSTR && *s == 'L') { + s++; + } + + quote = *s; + + if (quote != '"') { + + get_token (); + continue; + + } + + s++; + + while (*s && *s != '"') { + + unsigned int ch; + + if (*s == '\\') { + + s++; + + switch (*s) { + + case 'a': + + ch = '\a'; + + s++; + break; + + case 'b': + + ch = '\b'; + + s++; + break; + + case 'f': + + ch = '\f'; + + s++; + break; + + case 'n': + + ch = '\n'; + + s++; + break; + + case 'r': + + ch = '\r'; + + s++; + break; + + case 't': + + ch = '\t'; + + s++; + break; + + case 'v': + + ch = '\v'; + + s++; + break; + + case '\\': + + ch = '\\'; + + s++; + break; + + case '\'': + + ch = '\''; + + s++; + break; + + case '"': + + ch = '"'; + + s++; + break; + + case '?': + + ch = '?'; + + s++; + break; + + case 'x': + + s++; + + ch = (unsigned int) parse_hex_escape_value (&s); + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + + ch = (unsigned int) parse_octal_escape_value (&s); + break; + + case '\0': + + ch = 0; + break; + + default: + + ch = (unsigned char) *s; + + if (*s) { + s++; + } + + break; + + } + + } else { + ch = (unsigned char) *s++; + } + + append_global_init_byte (values, max_values, count, ch); + + } + + get_token (); + + } + + append_global_init_byte (values, max_values, count, 0); + +} + +static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width) { + + if (tok.kind == TOK_LBRACE) { + + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + parse_char_array_initializer_values (values, max_values, count, row_width); + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_RBRACE, "}"); + return; + + } + + if (is_string_token ()) { + + int64_s tmp[MAX_STRING_INIT_BYTES]; + int tmp_count = 0; + + int i; + int n; + + parse_string_initializer_values (tmp, MAX_STRING_INIT_BYTES, &tmp_count); + + if (row_width <= 0) { + n = tmp_count; + } else { + n = (tmp_count < row_width) ? tmp_count : (int) row_width; + } + + for (i = 0; i < n; i++) { + append_global_init_byte (values, max_values, count, (unsigned int) (tmp[i].low & 0xff)); + } + + while (row_width > 0 && i < row_width) { + + append_global_init_byte (values, max_values, count, 0); + i++; + + } + + return; + + } + + if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) { + + if (*count < max_values) { + values[*count] = const64_from_current_expr (); + } else { + const64_from_current_expr (); + } + + (*count)++; + + } + +} + +static int global_initializer_parenthesized_string_now (void) { + + const char *p = tok.caret; + + if (!p || *p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '"') { + return 1; + } + + if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') { + return 1; + } + + if (*p == 'u' && p[1] == '8' && p[2] == '"') { + return 1; + } + + return 0; + +} + +static int global_initializer_cast_before_string_now (void) { + + const char *p = tok.caret; + int depth; + + if (!p || *p != '(') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!(ident_start_now ((unsigned char) *p))) { + return 0; + } + + depth = 1; + + while (*p && depth > 0) { + + if (*p == '(') { + depth++; + } else if (*p == ')') { + depth--; + } + + p++; + + } + + if (depth != 0) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '"') { + return 1; + } + + if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') { + return 1; + } + + if (*p == 'u' && p[1] == '8' && p[2] == '"') { + return 1; + } + + return 0; + +} + +static void append_global_zero_initializer_value (int64_s *values, char **symbols, int max_values, int *count) { + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + symbols[*count] = 0; + } + + } + + (*count)++; + +} + +static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count) { + + int count = 0; + int i; + + for (i = 0; i < field_count; i++) { + + if (field_sizes[i] > 0) { + count++; + } + + } + + return count; + +} + +static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count) { + + if (element_field_count <= 0 || tok.kind != TOK_LBRACE) { + + parse_global_initializer_values (values, symbols, max_values, count); + return; + + } + + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + int before = *count; + parse_global_initializer_values (values, symbols, max_values, count); + + if (*count > before) { + + while ((*count - before) < element_field_count) { + append_global_zero_initializer_value (values, symbols, max_values, count); + } + + } + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_RBRACE, "}"); + +} + +static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count) { + + if (tok.kind == TOK_LBRACE) { + + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + parse_global_initializer_values (values, symbols, max_values, count); + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + expect (TOK_RBRACE, "}"); + return; + + } + + if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) { + + if (is_string_token ()) { + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + symbols[*count] = emit_string_literal_global (); + } + + } else { + get_token (); + } + + (*count)++; + + } else if (tok.kind == TOK_LPAREN && global_initializer_parenthesized_string_now ()) { + + get_token (); + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + symbols[*count] = emit_string_literal_global (); + } else { + + int64_s ignored_values[MAX_AGG_FIELDS]; + int ignored_count = 0; + + parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count); + + } + + } else { + + int64_s ignored_values[MAX_AGG_FIELDS]; + int ignored_count = 0; + + parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count); + + } + + expect (TOK_RPAREN, ")"); + (*count)++; + + } else if (tok.kind == TOK_LPAREN && global_initializer_cast_before_string_now ()) { + + int cast_size = 0; + int cast_is_unsigned = 0; + int cast_is_pointer = 0; + + get_token (); + + if (token_starts_type_name () && parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer) && is_string_token ()) { + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + symbols[*count] = emit_string_literal_global (); + } + + } else { + get_token (); + } + + (*count)++; + + } else { + + if (*count < max_values) { + + values[*count] = const64_from_current_expr (); + + if (symbols) { + symbols[*count] = 0; + } + + } else { + const64_from_current_expr (); + } + + (*count)++; + + } + + } else if (tok.kind == TOK_AMPER) { + + get_token (); + + if (tok.kind != TOK_IDENT) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'"); + } else { + + char addr_symbol[256]; + char *base_name = xstrdup (tok.ident); + long addr_offset = 0; + int sym_index = find_global_symbol (base_name); + long array_count = get_global_symbol_array_count (base_name); + long base_size = sym_index >= 0 ? global_symbols[sym_index].size : 0; + long elem_size = 0; + + if (array_count > 0 && base_size > 0) { + elem_size = base_size / array_count; + } + + get_token (); + + while (tok.kind == TOK_LBRACK) { + + int64_s index_value; + long index; + + get_token (); + + index_value.low = 0; + index_value.high = 0; + + if (tok.kind != TOK_RBRACK) { + index_value = const64_from_current_expr (); + } + + index = (long) index_value.low; + + if (elem_size > 0) { + addr_offset += index * elem_size; + } + + expect (TOK_RBRACK, "]"); + + } + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + + if (addr_offset != 0) { + + sprintf (addr_symbol, "%s+%ld", base_name, addr_offset); + symbols[*count] = xstrdup (addr_symbol); + + } else { + symbols[*count] = xstrdup (base_name); + } + + } + + } + + (*count)++; + free (base_name); + + } + + } else if (global_initializer_accept_symbol_addresses && tok.kind == TOK_IDENT && find_global_symbol (tok.ident) >= 0) { + + if (*count < max_values) { + + values[*count].low = 0; + values[*count].high = 0; + + if (symbols) { + symbols[*count] = xstrdup (tok.ident); + } + + } + + (*count)++; + get_token (); + + } else if (*count < max_values) { + + values[*count] = const64_from_current_expr (); + + if (symbols) { + symbols[*count] = 0; + } + + (*count)++; + + } else { + const64_from_current_expr (); + } + + } + +} + +static int int64_is_zero_value (int64_s value) { + return value.low == 0 && value.high == 0; +} + +static void emit_global_scalar (int64_s value, int size) { + + unsigned long high = value.high; + unsigned long low = value.low; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " db %ld\n", low & 0xFFUL); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " dw %ld\n", low & 0xFFFFUL); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + fprintf (state->ofp, " dd %lu\n", low & U32_MASK); + fprintf (state->ofp, " dd %lu\n", high & U32_MASK); + + } else { + fprintf (state->ofp, " dd %lu\n", low & U32_MASK); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " .byte %ld\n", low & 0xFFUL); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " .word %ld\n", low & 0xFFFFUL); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + fprintf (state->ofp, " .long %lu\n", low & U32_MASK); + fprintf (state->ofp, " .long %lu\n", high & U32_MASK); + + } else { + fprintf (state->ofp, " .long %lu\n", low & U32_MASK); + } + + } + +} + +static void emit_global_address (const char *symbol, int size) { + + const char *asm_symbol; + int64_s zero; + + if (!symbol || !*symbol) { + + zero.low = 0; + zero.high = 0; + + emit_global_scalar (zero, size); + return; + + } + + if (size != DATA_PTR) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "address initializer requires pointer-sized object"); + } + + emit_extern_reference_symbol (symbol, DATA_PTR); + asm_symbol = asm_global_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, " dd offset %s\n", asm_symbol); + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " dd %s\n", asm_symbol); + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " .long offset %s\n", asm_symbol); + } else { + fprintf (state->ofp, " .long %s\n", asm_symbol); + } + + } + +} + +static void emit_global_value (const int64_s *value, const char *symbol, int size) { + + int64_s zero; + + if (symbol) { + + emit_global_address (symbol, size); + return; + + } + + if (value) { + + emit_global_scalar (*value, size); + return; + + } + + zero.low = 0; + zero.high = 0; + + emit_global_scalar (zero, size); + +} + +static void emit_global_space (long bytes) { + + if (bytes <= 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, " db %ld dup (0)\n", bytes); + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " resb %ld\n", bytes); + } else { + fprintf (state->ofp, " .space %ld\n", bytes); + } + +} + +static int field_size_bytes (int field_size) { + + if (field_size < 0) { + return -field_size; + } + + return field_size; + +} + +static int global_initializer_is_all_zero (const int64_s *values, char **symbols, int value_count) { + + int i; + + if (symbols) { + for (i = 0; i < value_count; i++) { + if (symbols[i]) { + return 0; + } + } + } + + if (!values || value_count <= 0) { + return 1; + } + + for (i = 0; i < value_count; i++) { + if (!int64_is_zero_value (values[i])) { + return 0; + } + } + + return 1; + +} + +static void emit_global_label (const char *name, int is_static) { + + const char *asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_MASM) { + + if (!is_static) { + fprintf (state->ofp, "public %s\n", asm_name); + } + + fprintf (state->ofp, "%s:\n", asm_name); + + } else if (state->syntax & ASM_SYNTAX_NASM) { + + if (!is_static) { + fprintf (state->ofp, "global %s\n", asm_name); + } + + fprintf (state->ofp, "%s:\n", asm_name); + + } else { + + if (!is_static) { + fprintf (state->ofp, ".globl %s\n", asm_name); + } + + fprintf (state->ofp, "%s:\n", asm_name); + + } + +} + +static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static) { + + int64_s zero; + + int value_index, use_bss, i; + long emitted, total; + + zero.low = 0; + zero.high = 0; + + if (!state->ofp || !name || !*name) { + return; + } + + if (size <= 0) { + size = DATA_INT & 0x1f; + } + + if (array_count <= 0) { + array_count = 1; + } + + total = size * array_count; + use_bss = global_initializer_is_all_zero (values, symbols, value_count); + + if (use_bss) { + + switch_section (SECTION_BSS); + + emit_global_label (name, is_static); + emit_global_space (total); + + return; + + } + + switch_section (SECTION_DATA); + emit_global_label (name, is_static); + + value_index = 0; + emitted = 0; + + if (!is_array && !is_aggregate) { + + if (value_count > 0) { + emit_global_value (&values[0], symbols ? symbols[0] : 0, size); + } else { + emit_global_value (0, 0, size); + } + + return; + + } + + if (is_aggregate && field_count > 0) { + + while (emitted < total) { + + for (i = 0; i < field_count && emitted < total; i++) { + + int fsize = field_sizes[i]; + + if (fsize < 0) { + + emit_global_space (-fsize); + + emitted += -fsize; + continue; + + } + + if (value_index < value_count) { + + emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, fsize); + value_index++; + + } else { + emit_global_scalar (zero, fsize); + } + + emitted += field_size_bytes (fsize); + + } + + } + + emit_global_space (total - emitted); + return; + + } + + if (is_array) { + + int elem_size = size; + + if (field_count > 0 && field_sizes[0] > 0) { + elem_size = field_sizes[0]; + } + + while (emitted + elem_size <= total) { + + if (value_index < value_count) { + + emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, elem_size); + value_index++; + + } else { + emit_global_scalar (zero, elem_size); + } + + emitted += elem_size; + + } + + emit_global_space (total - emitted); + return; + + } + + emit_global_space (total); + +} + +static char *emit_string_literal_global (void) { + + int64_s values[MAX_AGG_FIELDS]; + + char label[64]; + int value_count = 0, i; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (label, "L%d", anon_label++); + } else { + sprintf (label, ".L%d", anon_label++); + } + + parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count); + + /* + * Dead statement parsing suppresses output by temporarily clearing + * state->ofp. Still consume the literal so the token stream remains + * correct, but do not try to emit a .data label through a NULL FILE *. + * This is needed for optimised-away bodies such as: + * + * while (0) { printf ("Hello\n"); } + */ + if (!state->ofp) { + return xstrdup (label); + } + + if (current_section == SECTION_TEXT) { + + char skip_label[64]; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (skip_label, "L%d", anon_label++); + } else { + sprintf (skip_label, ".L%d", anon_label++); + } + + fprintf (state->ofp, " jmp %s\n", skip_label); + + switch_section (SECTION_DATA); + emit_global_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], DATA_CHAR & 0x1f); + } + + switch_section (SECTION_TEXT); + emit_global_label (skip_label, 1); + + return xstrdup (label); + + } + + switch_section (SECTION_DATA); + emit_global_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], DATA_CHAR & 0x1f); + } + + return xstrdup (label); + +} + +static const char *masm_extern_type_name (int size, int is_function) { + + if (is_function) { + return "PROC"; + } + + if (size == (DATA_CHAR & 0x1f)) { + return "BYTE"; + } + + if (size == (DATA_SHORT & 0x1f)) { + return "WORD"; + } + + if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + return "QWORD"; + } + + return "DWORD"; + +} + +static void emit_extern_line (const char *name, int size, int is_function) { + + const char *asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, "extrn %s:%s\n", asm_name, masm_extern_type_name (size, is_function)); + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "extern %s\n", asm_name); + } else { + fprintf (state->ofp, ".extern %s\n", asm_name); + } + +} + +static void emit_extern_symbol (const char *name, int size, int is_function) { + + int i; + + (void) size; + (void) is_function; + + if (!state->ofp || !name || !*name) { + return; + } + + i = find_global_symbol (name); + + /** + * Do not write assembler externs at the point of use. A symbol can be + * declared/called before its real definition appears later in the same + * translation unit; NASM then rejects "extern foo" followed by "foo:" as + * an inconsistent redefinition. Just mark that generated code referenced + * the external-looking symbol, and emit the actual assembler externs once + * the whole file has been parsed and we know which names stayed external. + */ + if (i < 0 || !global_symbols[i].is_extern) { + return; + } + + global_symbols[i].extern_emitted = 1; + +} + +static void emit_pending_extern_symbols (void) { + + int i; + + if (!state->ofp) { + return; + } + + for (i = 0; i < global_symbol_count; i++) { + + if (!global_symbols[i].is_extern || !global_symbols[i].extern_emitted) { + continue; + } + + emit_extern_line (global_symbols[i].name, + global_symbols[i].size > 0 ? global_symbols[i].size : (DATA_INT & 0x1f), + global_symbols[i].kind == GLOBAL_SYMBOL_FUNCTION); + + } + +} + +static void emit_extern_reference_symbol (const char *name, int size) { + emit_extern_symbol (name, size, get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION); +} + +static void parse_external_after_type (void) { + + for (;;) { + + char **init_symbols = 0; + int64_s *init_values = 0; + + char *name = 0; + int init_value_count = 0, i; + + int object_fields[MAX_AGG_FIELDS]; + int object_field_count = 0; + + const char *name_start, *name_caret; + unsigned long name_line; + + int saved_field_count = parsed_field_count; + + int declaration_is_inline; + int declaration_storage; + + int decl_is_pointer; + int decl_pointer_depth; + int decl_has_array; + int decl_has_function; + int decl_function_is_pointer; + int decl_function_param_count; + int decl_function_has_prototype; + int decl_function_is_variadic; + int decl_array_unsized; + + long decl_array_count; + long decl_last_array_count; + + for (i = 0; i < saved_field_count; i++) { + object_fields[i] = parsed_field_sizes[i]; + } + + object_field_count = saved_field_count; + + declaration_is_inline = parsed_type_is_inline; + declaration_storage = parsed_storage_class; + + parse_declarator (&name); + apply_typedef_array_to_declarator (); + + decl_is_pointer = declarator_is_pointer; + decl_pointer_depth = declarator_pointer_depth; + decl_has_array = declarator_has_array; + decl_has_function = declarator_has_function; + decl_function_is_pointer = declarator_function_is_pointer; + decl_function_param_count = declarator_function_param_count; + decl_function_has_prototype = declarator_function_has_prototype; + decl_function_is_variadic = declarator_function_is_variadic; + decl_array_unsized = declarator_array_unsized; + decl_array_count = declarator_array_count; + decl_last_array_count = declarator_last_array_count; + + name_start = last_declarator_name_start; + name_caret = last_declarator_name_caret; + + name_line = last_declarator_name_line; + + if (declaration_storage == STORAGE_TYPEDEF) { + + if (name) { + + make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate); + + save_typedef_name (name, declarator_object_size (parsed_type_size), + (declarator_is_pointer ? 0 : parsed_type_is_unsigned), + (declarator_is_pointer ? 0 : parsed_type_is_void), + (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)), + (!declarator_is_pointer && declarator_has_array), + declarator_array_count, parsed_type_size, + object_fields, object_field_count); + + } + + if (_accept (TOK_ASSIGN)) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "typedef '%s' is initialized", name ? name : ""); + skip_initializer (); + + } + + if (name) { + free (name); + } + + if (!_accept (TOK_COMMA)) { + break; + } + + continue; + + } + + if (name && tok.kind == TOK_LBRACE) { + + parse_function_body (name, declaration_storage, declaration_is_inline, + (parsed_type_is_void && !declarator_is_pointer), + (declarator_is_pointer ? 0 : parsed_type_is_floating), + (declarator_is_pointer ? 0 : parsed_type_is_unsigned), + (declarator_is_pointer ? DATA_PTR : parsed_type_size), + name_line, name_start, name_caret); + + free (name); + return; + + } + + if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) { + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : ""); + } + + if (declarator_is_pointer) { + + object_field_count = 1; + object_fields[0] = DATA_PTR; + + } + + init_values = xmalloc (sizeof (*init_values) * MAX_GLOBAL_INIT_FIELDS); + init_symbols = xmalloc (sizeof (*init_symbols) * MAX_GLOBAL_INIT_FIELDS); + + for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) { + init_symbols[i] = 0; + } + + if (_accept (TOK_ASSIGN)) { + + if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) { + + if (is_string_token ()) { + parse_string_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count); + } else { + parse_char_array_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count, declarator_last_array_count); + } + + if (decl_array_unsized) { + decl_array_count = init_value_count; + } + + } else if (declarator_is_pointer && is_string_token ()) { + + init_values[0].low = 0; + init_values[0].high = 0; + + init_symbols[0] = emit_string_literal_global (); + init_value_count = 1; + + } else if (!declarator_is_pointer && parsed_type_is_floating) { + + init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size); + init_symbols[0] = 0; + init_value_count = 1; + + } else { + + { + + int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses; + global_initializer_accept_symbol_addresses = declarator_is_pointer || declarator_has_function || parsed_type_is_aggregate || declarator_has_array; + + if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) { + parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count)); + } else { + parse_global_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count); + } + + global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses; + + } + + if (decl_has_array && decl_array_unsized) { + + if (parsed_type_is_aggregate && object_field_count > 0) { + decl_array_count = (init_value_count + object_field_count - 1) / object_field_count; + } else { + decl_array_count = init_value_count; + } + + } + + } + + } + + if (init_value_count > MAX_GLOBAL_INIT_FIELDS) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "too many initializer values for '%s'", name ? name : ""); + init_value_count = MAX_GLOBAL_INIT_FIELDS; + + } + + declarator_is_pointer = decl_is_pointer; + declarator_pointer_depth = decl_pointer_depth; + declarator_has_array = decl_has_array; + declarator_has_function = decl_has_function; + declarator_function_is_pointer = decl_function_is_pointer; + declarator_function_param_count = decl_function_param_count; + declarator_function_has_prototype = decl_function_has_prototype; + declarator_function_is_variadic = decl_function_is_variadic; + declarator_array_unsized = decl_array_unsized; + declarator_array_count = decl_array_count; + declarator_last_array_count = decl_last_array_count; + + if (name) { + + if (declarator_has_function && !declarator_function_is_pointer && !declarator_has_array) { + + /* + * A file-scope function declaration is a declaration even + * without an explicit extern storage class. Keep it in the + * global symbol table so a later call does not fall back to + * the implicit-function-declaration path. + * + * Store prototypes as extern-like declarations here. A real + * function body will later turn the symbol into a definition + * through add_global_symbol(), which already handles replacing + * an extern declaration with the definition. + */ + if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line)) { + + set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : (parsed_type_is_void ? DATA_VOID : parsed_type_size)); + set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + set_global_symbol_tag_name (name, parsed_type_tag_name); + set_global_symbol_unsigned (name, 0); + set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating); + set_global_symbol_returns_void (name, parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer); + set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic); + + } + + } else if (declaration_storage == STORAGE_EXTERN && init_value_count == 0) { + + if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) { + + set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size)); + set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + set_global_symbol_tag_name (name, parsed_type_tag_name); + set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned); + set_global_symbol_array (name, declarator_has_array); + set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0); + set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0); + + } + + } else { + + if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) { + + set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size)); + set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (), + declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count)); + set_global_symbol_tag_name (name, parsed_type_tag_name); + set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned); + set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating); + set_global_symbol_array (name, declarator_has_array); + set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0); + set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0); + + emit_global_object (name, declarator_is_pointer ? DATA_PTR : (declarator_has_array ? parsed_type_size : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))), + declarator_has_array, declarator_array_count, object_fields, object_field_count, + init_values, init_symbols, init_value_count, (!declarator_is_pointer && parsed_type_is_aggregate), + declaration_storage == STORAGE_STATIC); + + } + + } + + free (name); + + } + + for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) { + + if (init_symbols[i]) { + + free (init_symbols[i]); + init_symbols[i] = 0; + + } + + } + + free (init_symbols); + free (init_values); + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + if (tok.kind == TOK_SEMI) { + get_token (); + } else if (is_type_start (tok.kind)) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing ';' after declaration"); + } else { + expect (TOK_SEMI, ";"); + } + +} + +void compile_translation_unit (void) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + + fprintf (state->ofp, ".386\n"); + fprintf (state->ofp, ".model flat, c\n"); + + } else if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, "cpu 386\n"); + fprintf (state->ofp, "bits 32\n"); + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, ".intel_syntax noprefix\n"); + } + + } + + current_section = SECTION_NONE; + + clear_global_symbols (); + clear_inline_functions (); + clear_enum_constants (); + clear_typedef_names (); + + get_token (); + + while (tok.kind != TOK_EOF) { + + if (is_type_start (tok.kind)) { + + parse_type_spec (); + parse_external_after_type (); + + continue; + + } + + if (parse_possible_knr_function ()) { + continue; + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected external declaration"); + get_token (); + + } + + emit_pending_extern_symbols (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, "end\n"); + } + + } + +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..ca59939 --- /dev/null +++ b/parse.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * @file parse.h + *****************************************************************************/ +#ifndef _PARSE_H +#define _PARSE_H + +void compile_translation_unit (void); + +int token_is_sizeof_keyword (void); +int parse_sizeof_value (void); + +int token_starts_type_name (void); +int parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer); + +int parse_constexpr_address_of_null_member (int64_s *out); +int resolve_enum_constant (const char *name, int64_s *out); + +#endif /* _PARSE_H */ \ No newline at end of file diff --git a/pp.c b/pp.c new file mode 100755 index 0000000..18de06d --- /dev/null +++ b/pp.c @@ -0,0 +1,2031 @@ +/****************************************************************************** + * @file pp.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "cc.h" +#include "eval.h" +#include "hashtab.h" +#include "lex.h" +#include "lib.h" +#include "ll.h" +#include "macro.h" +#include "pp.h" +#include "report.h" +#include "token.h" +#include "vector.h" + +static void hash_line (const char *filename, unsigned long line_number); +static int flags = 0; + +static int iflevel = 0; +int ignore_line = 0; + +struct vector vec_include_paths = { 0 }; +struct vector vec_ifstack = { 0 }; + +static int handler_if (char *start, char **pp) { + + struct cond *cond; + int ret = 1; + + if (!ignore_line) { + + cond = xmalloc (sizeof (*cond)); + + cond->ignore_line = ignore_line; + cond->directive = xstrdup ("if"); + + cond->filename = xstrdup (get_filename ()); + cond->line_number = get_line_number (); + + vec_push (&vec_ifstack, cond); + ret = !eval (start, pp); + + cond->need_else = !ret; + + } else { + iflevel++; + } + + return ret; + +} + +static int handler_ifdef (char *start, char **pp) { + + struct cond *cond; + + char *sname, *caret; + int ret = 1; + + if (!ignore_line) { + + cond = xmalloc (sizeof (*cond)); + + cond->ignore_line = ignore_line; + cond->directive = xstrdup ("ifdef"); + + cond->filename = xstrdup (get_filename ()); + cond->line_number = get_line_number (); + + vec_push (&vec_ifstack, cond); + *pp = skip_whitespace (*pp); + + if (is_name_beginner ((int) **pp)) { + + caret = (*pp); + + while (is_name_part ((int) **pp)) { + (*pp)++; + } + + sname = xstrndup (caret, *pp - caret); + ret = (find_macro (sname) == NULL); + + free (sname); + + } + + *pp = skip_whitespace (*pp); + + if (!is_end_of_line[(int) **pp]) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #ifdef directive"); + } + + cond->need_else = !ret; + + } else { + iflevel++; + } + + return ret; + +} + +static int handler_ifndef (char *start, char **pp) { + + struct cond *cond; + + char *sname, *caret; + int ret = 1; + + if (!ignore_line) { + + cond = xmalloc (sizeof (*cond)); + + cond->ignore_line = ignore_line; + cond->directive = xstrdup ("ifndef"); + + cond->filename = xstrdup (get_filename ()); + cond->line_number = get_line_number (); + + vec_push (&vec_ifstack, cond); + *pp = skip_whitespace (*pp); + + if (is_name_beginner ((int) **pp)) { + + caret = (*pp); + + while (is_name_part ((int) **pp)) { + (*pp)++; + } + + sname = xstrndup (caret, *pp - caret); + ret = !(find_macro (sname) == NULL); + + free (sname); + + } + + *pp = skip_whitespace (*pp); + + if (!is_end_of_line[(int) **pp]) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #ifndef directive"); + } + + cond->need_else = !ret; + + } else { + iflevel++; + } + + return ret; + +} + +static int handler_elif (char *start, char **pp) { + + struct cond *cond; + int ret = 1; + + if (!iflevel) { + + if (vec_ifstack.length == 0) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#elif without #if"); + return ret; + + } else { + + cond = vec_ifstack.data[vec_ifstack.length - 1]; + + if (cond->has_else > 0) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#elif after #else"); + return ret; + + } + + } + + if (ignore_line) { + ret = !eval (start, pp); + } + + cond->need_else = !ret || !ignore_line; + + } + + return ret; + +} + +static int handler_else (char *start, char **pp) { + + struct cond *cond; + int ret = 1; + + if (!iflevel) { + + if (vec_ifstack.length == 0) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#else without #if"); + return ret; + + } else { + + cond = vec_ifstack.data[vec_ifstack.length - 1]; + + if (cond->has_else > 0) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#else after #else"); + return ret; + + } + + cond->has_else++; + + } + + *pp = skip_whitespace (*pp); + + if (!is_end_of_line[(int) **pp]) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #else directive"); + } + + ret = cond->need_else; + + } + + return ret; + +} + +static int handler_endif (char *start, char **pp) { + + struct cond *cond; + int ret = 1; + + if (!iflevel) { + + if ((cond = vec_pop (&vec_ifstack))) { + + ret = cond->ignore_line; + + free (cond->filename); + free (cond->directive); + + free (cond); + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "#endif without #if"); + } + + *pp = skip_whitespace (*pp); + + if (!is_end_of_line[(int) **pp]) { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, *pp, "extra tokens at end of #endif directive"); + } + + } else { + iflevel--; + } + + return ret; + +} + +static struct pp_pseudo_op_entry cond_pseudo_op_table[] = { + + { "if", &handler_if, }, + { "ifdef", &handler_ifdef }, + { "ifndef", &handler_ifndef }, + { "elif", &handler_elif }, + { "else", &handler_else }, + { "endif", &handler_endif }, + + { 0, 0 } + +}; + +static struct hashtab hashtab_cond_pseudo_ops = { 0 }; +static int includes = 0; + +static void install_cond_pseudo_op_table (struct pp_pseudo_op_entry *table) { + + struct pp_pseudo_op_entry *entry; + struct hashtab_name *key; + + for (entry = table; entry->name; entry++) { + + if (hashtab_get_key (&hashtab_cond_pseudo_ops, entry->name)) { + + report_at (program_name, 0, REPORT_ERROR, "duplicate entry '%s'", entry->name); + continue; + + } + + if (!(key = hashtab_alloc_name (entry->name))) { + + report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", entry->name); + continue; + + } + + hashtab_put (&hashtab_cond_pseudo_ops, key, entry); + + } + +} + +struct pp_pseudo_op_entry *find_cond_directive (char *name) { + + struct hashtab_name *key; + struct pp_pseudo_op_entry *entry; + + if ((key = hashtab_get_key (&hashtab_cond_pseudo_ops, name))) { + + if ((entry = hashtab_get (&hashtab_cond_pseudo_ops, key))) { + return entry; + } + + } + + return 0; + +} + + +static struct hashtab hashtab_pseudo_ops = { 0 }; + +static int handler_define (char *start, char **pp) { + + add_macro (start, pp, 1); + return 1; + +} + +static int handler_error (char *start, char **pp) { + + unsigned int len = strlen (*pp); + char *temp, *type = "error"; + + if ((*pp)[len - 1] == '\n') { + (*pp)[len - 1] = '\0'; + } + + temp = xmalloc (1 + strlen (type) + 1 + strlen (*pp) + 1); + sprintf (temp, "#%s %s", type, *pp); + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, skip_whitespace (start + 1), "%s", temp); + free (temp); + + return 1; + +} + +static int handler_include (char *start, char **pp) { + + const char *orig_fn = get_filename (); + unsigned long orig_ln = get_line_number (); + + char *caret, *sname, ch; + int i; + + char *inc_path, *tmp; + FILE *fp; + + struct hashtab_name *key; + struct macro *m; + + char *current_directory,*p; + int len; + + if (**pp != '"' && **pp != '<') { + + report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, *pp, "#include expects \"FILENAME\" or "); + return 1; + + } + + ch = (**pp == '"' ? '"' : '>'); + caret = (*pp)++; + + while (!is_end_of_line[(int) **pp]) { + + if (**pp == ch) { break; } + (*pp)++; + + } + + if (**pp != ch) { + + report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, caret, "#include expects \"FILENAME\" or "); + return 1; + + } else { + (*pp)++; + } + + sname = xstrndup (caret + 1, *pp - caret - 2); + + if (ch == '"') { + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + if (sname[0] == '/') { +#else + if ((isalpha ((int) sname[0]) && sname[1] == ':') || sname[0] == '/' || sname[0] == '\\') { +#endif + + if ((fp = fopen (sname, "r"))) { + + flags = 1; + fclose (fp); + + includes++; + preprocess_file (sname); + + includes--; + + flags = 2; + goto end; + + } + + goto error; + + } else if ((p = strrchr (orig_fn, '/')) || (p = strrchr (orig_fn, '\\'))) { + + len = p - orig_fn; + + tmp = xmalloc (len + 1 + strlen (sname) + 1); + sprintf (tmp, "%.*s/%s", len, orig_fn, sname); + + if ((fp = fopen (tmp, "r"))) { + + flags = 1; + fclose (fp); + + includes++; + preprocess_file (tmp); + + includes--; + free (tmp); + + flags = 2; + goto end; + + } + + free (tmp); + + } + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + if (orig_fn[0] == '/') { +#else + if ((isalpha ((int) orig_fn[0]) && orig_fn[1] == ':') || orig_fn[0] == '/' || orig_fn[0] == '\\') { +#endif + + if ((current_directory = get_current_directory ())) { + + if (strcmp (current_directory, "") == 0) { + + tmp = xmalloc (strlen (sname) + 1); + sprintf (tmp, "%s", sname); + + } else { + + tmp = xmalloc (strlen (current_directory) + 1 + strlen (sname) + 1); + sprintf (tmp, "%s/%s", current_directory, sname); + + } + + free (current_directory); + + if ((fp = fopen (tmp, "r"))) { + + flags = 1; + fclose (fp); + + includes++; + preprocess_file (tmp); + + includes--; + free (tmp); + + flags = 2; + goto end; + + } + + free (tmp); + + } + + } else { + + if ((fp = fopen (sname, "r"))) { + + flags = 1; + fclose (fp); + + includes++; + preprocess_file (sname); + + includes--; + + flags = 2; + goto end; + + } + + } + + } + + if (vec_include_paths.length > 0) { + + for (i = 0; i < vec_include_paths.length; i++) { + + inc_path = vec_include_paths.data[i]; + + tmp = xmalloc (strlen (inc_path) + strlen (sname) + 1); + sprintf (tmp, "%s%s", inc_path, sname); + + if ((fp = fopen (tmp, "r"))) { + + flags = 1; + fclose (fp); + + includes++; + preprocess_file (tmp); + + includes--; + free (tmp); + + flags = 2; + goto end; + + } + + free (tmp); + + } + + } + +error: + + report_line_at (orig_fn, orig_ln, REPORT_ERROR, start, caret, "failed to open '%s' for reading", sname); + +end: + + set_filename_and_line_number (orig_fn, orig_ln); + + if ((key = find_macro ("__FILE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (1 + strlen (orig_fn) + 2); + sprintf (m->value, "\"%s\"", orig_fn); + + } + + } + + if ((key = find_macro ("__LINE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (16); + sprintf (m->value, "%lu", orig_ln); + + } + + } + + free (sname); + return 0; + +} + +static int handler_undef (char *start, char **pp) { + + remove_macro (start, pp, 1); + return 1; + +} + +static int handler_warning (char *start, char **pp) { + + unsigned int len = strlen (*pp); + char *temp, *type = "warning"; + + if ((*pp)[len - 1] == '\n') { + (*pp)[len - 1] = '\0'; + } + + temp = xmalloc (1 + strlen (type) + 1 + strlen (*pp) + 1); + sprintf (temp, "#%s %s", type, *pp); + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, skip_whitespace (start + 1), "%s", temp); + free (temp); + + return 1; + +} + +static struct pp_pseudo_op_entry pseudo_op_table[] = { + + { "define", &handler_define }, + { "error", &handler_error }, + { "undef", &handler_undef }, + { "warning", &handler_warning }, + + { 0, 0 } + +}; + +static void install_pp_pseudo_op_table (struct pp_pseudo_op_entry *table) { + + struct pp_pseudo_op_entry *entry; + struct hashtab_name *key; + + for (entry = table; entry->name; entry++) { + + if (hashtab_get_key (&hashtab_pseudo_ops, entry->name)) { + + report_at (program_name, 0, REPORT_ERROR, "duplicate entry '%s'", entry->name); + continue; + + } + + if (!(key = hashtab_alloc_name (entry->name))) { + + report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", entry->name); + continue; + + } + + hashtab_put (&hashtab_pseudo_ops, key, entry); + + } + +} + +struct pp_pseudo_op_entry *find_directive (char *name) { + + struct hashtab_name *key; + struct pp_pseudo_op_entry *entry; + + if ((key = hashtab_get_key (&hashtab_pseudo_ops, name))) { + + if ((entry = hashtab_get (&hashtab_pseudo_ops, key))) { + return entry; + } + + } + + return 0; + +} + + +static void init_builtin_macros (void) { + + static char *builtins[] = { "__FILE__", "__LINE__" }; + char *name; + + struct hashtab_name *key; + struct macro *m; + + unsigned int cnt = (sizeof (builtins) / sizeof (*builtins)); + unsigned i; + + for (i = 0; i < cnt; i++) { + + name = xstrdup (builtins[i]); + + if ((key = hashtab_alloc_name (name))) { + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + m->value = xstrdup (""); + + m->nargs = -1; + push_macro (key, m); + + } + + } + + name = xstrdup ("__SCC__"); + + if ((key = hashtab_alloc_name (name))) { + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + m->value = "1"; + + m->nargs = -1; + push_macro (key, m); + + } + +#if defined (VERSION) + + name = xstrdup ("__SCC_VERSION__"); + + if ((key = hashtab_alloc_name (name))) { + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + + m->value = xmalloc (10); + sprintf (m->value, "%ldL", state->version); + + m->nargs = -1; + push_macro (key, m); + + } + +#endif + + if (state->std) { + + name = xstrdup ("__STDC__"); + + if ((key = hashtab_alloc_name (name))) { + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + m->value = "1"; + + m->nargs = -1; + push_macro (key, m); + + } + + } + +} + +static void skip_pp_quoted (char **pp, char *end) { + + char quote = **pp; + (*pp)++; + + while (*pp < end) { + + if (**pp == '\\') { + + (*pp)++; + + if (*pp < end) { + (*pp)++; + } + + continue; + + } + + if (**pp == quote) { + + (*pp)++; + break; + + } + + (*pp)++; + + } + +} + +static int line_has_unclosed_function_macro (char *line, char *line_end) { + + char *p = line, *q, *r, *sname; + int depth; + + struct hashtab_name *key; + struct macro *m; + + while (p < line_end) { + + if (*p == '"' || *p == '\'') { + + skip_pp_quoted (&p, line_end); + continue; + + } + + if (!is_name_beginner ((int) *p)) { + + p++; + continue; + + } + + q = p; + sname = symname (&q); + + if ((key = find_macro (sname)) && (m = get_macro (key)) && m->nargs >= 0) { + + r = skip_whitespace (q); + + if (r < line_end && *r == '(') { + + depth = 0; + + while (r < line_end) { + + if (*r == '"' || *r == '\'') { + + skip_pp_quoted (&r, line_end); + continue; + + } + + if (*r == '(') { + depth++; + } else if (*r == ')') { + + depth--; + + if (depth == 0) { + break; + } + + } + + r++; + + } + + free (sname); + + if (depth > 0) { + return 1; + } + + p = r; + continue; + + } + + } + + free (sname); + p = q; + + } + + return 0; + +} + +static char *collect_macro_continuation (char *line, char **line_end_p, FILE *fp, void **load_line_internal_data_p, unsigned long *new_line_number_p) { + + char *buf, *next, *next_end; + unsigned long len, next_len, cap, newlines; + + len = *line_end_p - line; + cap = len + 1; + + buf = xmalloc (cap + 1); + memcpy (buf, line, len); + + buf[len] = '\0'; + + while (line_has_unclosed_function_macro (buf, buf + len)) { + + if (load_line (&next, &next_end, 0, 0, &newlines, fp, load_line_internal_data_p)) { + break; + } + + next_len = next_end - next; + + if (len + 1 + next_len + 1 > cap) { + + cap = len + 1 + next_len + 1 + 256; + buf = xrealloc (buf, cap + 1); + + } + + buf[len++] = ' '; + memcpy (buf + len, next, next_len); + + len += next_len; + buf[len] = '\0'; + + *new_line_number_p += newlines + 1; + + } + + *line_end_p = buf + len; + return buf; + +} + +static void init_date_time_macros (void) { + + char *timep, *buf, *name; + time_t now; + + struct hashtab_name *key; + struct macro *m; + + time (&now); + timep = ctime (&now); + + name = xstrdup ("__TIME__"); + + if ((key = hashtab_alloc_name (name))) { + + buf = xmalloc (11); + sprintf (buf, "\"%.8s\"", timep + 11);; + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + m->value = buf; + + m->nargs = -1; + push_macro (key, m); + + } + + name = xstrdup ("__DATE__"); + + if ((key = hashtab_alloc_name (name))) { + + buf = xmalloc (14); + sprintf (buf, "\"%.3s %.2s %.4s\"", timep + 4, timep + 8, timep + 20); + + m = xmalloc (sizeof (*m)); + m->type = MACRO_BUILTIN; + + m->name = name; + m->value = buf; + + m->nargs = -1; + push_macro (key, m); + + } + +} + +int preprocess_init (void) { + + struct list *item; + char *opt, *nopt, *p; + + if (state->mode == CC_MODE_PREPROCESS) { + + hash_line (state->ifile, 0); + hash_line ("", 0); + + } + + init_builtin_macros (); + init_date_time_macros (); + + if (state->mode == CC_MODE_PREPROCESS) { + hash_line ("", 0); + } + + set_filename_and_line_number (xstrdup (""), 1); + + if (state->pplist) { + + item = state->pplist; + + do { + + item = item->next; + + if (!(opt = item->data)) { + continue; + } + + if (opt[0] != '-') { + + report_at (program_name, 0, REPORT_ERROR, "unrecognised option '%s'", opt); + continue; + + } + + switch (opt[1]) { + + case 'D': + + opt = nopt = xstrdup (opt + 2); + + if ((p = strrchr (nopt, '='))) { + *p++ = ' '; + } + + add_macro (nopt, &nopt, 0); + free (opt); + + break; + + case 'I': + + vec_push (&vec_include_paths, xstrdup (opt + 2)); + break; + + case 'U': + + opt = nopt = xstrdup (opt + 2); + + remove_macro (nopt, &nopt, 0); + free (opt); + + break; + + default: + + report_at (program_name, 0, REPORT_ERROR, "unrecognised option '%s'", opt); + break; + + } + + } while (item != state->pplist); + + } + + install_cond_pseudo_op_table (cond_pseudo_op_table); + install_pp_pseudo_op_table (pseudo_op_table); + + return get_error_count () > 0; + +} + + +static struct hashtab hashtab_seen_macros = { 0 }; +static struct vector vector_tokens = { 0 }; + +struct vector *preprocess_line (char **line, char *real_start, char *real_caret, int in_macro) { + + char *caret = *line, *start; + char *sname, ch; + + struct hashtab_name *key; + struct macro *m; + + struct token *tok; + + while (!is_end_of_line[(int) *caret]) { + + start = caret; + + if (isspace ((int) *caret)) { + + while (isspace ((int) *caret)) { + + if (state->mode == CC_MODE_PREPROCESS && state->ofp) { + fprintf (state->ofp, " "); + } + + caret++; + + } + + continue; + + } + + if (*caret == 'L' && (*(caret + 1) == '\'' || *(caret + 1) == '"' || *(caret + 1) == '\\')) { + + ch = *(caret + 1); + caret += 2; + + while (!is_end_of_line[(int) *caret]) { + + if (*caret == '\\') { + + caret++; + + if (!is_end_of_line[(int) *caret]) { + caret++; + } + + continue; + + } + + if (*caret == ch) { break; } + caret++; + + } + + if (*caret != ch) { + + char *temp = xmalloc ((caret - start) + 2); + sprintf (temp, "%.*s%c", (int) (caret - start), start, ch); + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, *line, start, "missing terminating %c character", ch); + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%s", temp); + } + + free (temp); + continue; + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = (ch == '"' ? TOK_LSTR : TOK_LCHAR); + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = temp; + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + continue; + + } + + caret++; + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%.*s", (int) (caret - start), start); + } + + continue; + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = (ch == '"' ? TOK_LSTR : TOK_LCHAR); + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = xstrndup (start, (int) (caret - start)); + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + continue; + + } + + if (*caret == '"' || *caret == '\'') { + + ch = *caret++; + + while (!is_end_of_line[(int) *caret]) { + + if (*caret == '\\') { + + caret++; + + if (!is_end_of_line[(int) *caret]) { + caret++; + } + + continue; + + } + + if (*caret == ch) { break; } + caret++; + + } + + if (*caret != ch) { + + char *temp = xmalloc ((caret - start) + 2); + sprintf (temp, "%.*s%c", (int) (caret - start), start, ch); + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, *line, start, "missing terminating %c character", ch); + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%s", temp); + } + + free (temp); + continue; + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = (ch == '"' ? TOK_PPSTR : TOK_CCHAR); + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = temp; + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + continue; + + } + + caret++; + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%.*s", (int) (caret - start), start); + } + + continue; + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = (ch == '"' ? TOK_PPSTR : TOK_CCHAR); + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = xstrndup (start, (int) (caret - start)); + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + continue; + + } + + if (is_name_beginner ((int) *caret)) { + + sname = symname (&caret); + + if (!(key = hashtab_get_key (&hashtab_seen_macros, sname))) { + + char *pm; + + if ((key = find_macro (sname))) { + + hashtab_put (&hashtab_seen_macros, key, sname); + + if ((m = get_macro (key))) { + + if (m->nargs >= 0) { + caret = skip_whitespace (caret); + } + + if ((pm = process_macro (start, &caret, m))) { + + if (m->nargs < 0 && !is_end_of_line[(int) *caret]) { + + char *p = skip_whitespace (pm); + char *q = p; + char *alias = 0; + char *tail = skip_whitespace (caret); + + struct hashtab_name *alias_key; + struct macro *alias_macro; + + if (is_name_beginner ((int) *p)) { + + alias = symname (&q); + q = skip_whitespace (q); + + if (is_end_of_line[(int) *q] + && *tail == '(' + && (alias_key = find_macro (alias)) + && (alias_macro = get_macro (alias_key)) + && alias_macro->nargs >= 0) { + + char *combined; + size_t pm_len = strlen (pm); + size_t tail_len = strlen (caret); + + combined = xmalloc (pm_len + tail_len + 1); + memcpy (combined, pm, pm_len); + memcpy (combined + pm_len, caret, tail_len + 1); + + pm = combined; + caret += tail_len; + + } + + free (alias); + + } + + } + + preprocess_line (&pm, real_start ? real_start : *line, real_caret ? real_caret : start, m->nargs >= 0 ? 2 : 1); + + } + + while (!is_end_of_line[(int) *caret]) { + + if (!isspace (*caret)) { + break; + } + + if (state->mode == CC_MODE_PREPROCESS) { + putc (*caret, state->ofp); + } + + caret++; + + } + + } + + hashtab_remove (&hashtab_seen_macros, key); + + /*while (!is_end_of_line[(int) *caret]) { + + if (isspace ((int) *caret)) { + break; + } + + if (state->mode == CC_MODE_PREPROCESS) { + fputc (*caret, state->ofp); + } + + caret++; + + }*/ + + continue; + + } + + } + + free (sname); + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%.*s", (int) (caret - start), start); + } + + continue; + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = TOK_IDENT; + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = xstrndup (start, (int) (caret - start)); + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + continue; + + } + + if (caret[0] == '0' && tolower ((int) caret[1]) == 'x') { + + caret += 2; + + while (isxdigit ((int) *caret)) { + caret++; + } + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%.*s", (int) (caret - start), start); + } + + continue; + + } + + if (toupper ((int) *caret) == 'U') { + + caret++; + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + } else if (toupper ((int) *caret) == 'L') { + + caret++; + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + if (toupper ((int) *caret) == 'U') { + caret++; + } + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = TOK_PPNUM; + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = xstrndup (start, (int) (caret - start)); + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + + if (is_name_part ((int) *caret)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, *line, caret, "unexpected '%c' character", *caret); + + while (is_name_part ((int) *caret)) { + caret++; + } + + } + + continue; + + } + + if (isdigit ((int) *caret) || (caret[0] == '.' && isdigit ((int) caret[1]))) { + + int saw_dot = 0, saw_exp = 0; + + while (!is_end_of_line[(int) *caret]) { + + if (isdigit ((int) *caret)) { + + caret++; + continue; + + } + + if (*caret == '.' && !saw_dot && !saw_exp) { + + saw_dot = 1; + caret++; + + continue; + + } + + if ((*caret == 'e' || *caret == 'E' || *caret == 'p' || *caret == 'P') && !saw_exp) { + + saw_exp = 1; + caret++; + + if (*caret == '+' || *caret == '-') { + caret++; + } + + continue; + + } + + break; + + } + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fprintf (state->ofp, "%.*s", (int) (caret - start), start); + } + + continue; + + } + + if (saw_dot || saw_exp) { + + if (tolower ((int) *caret) == 'f') { + caret++; + } else if (toupper ((int) *caret) == 'L') { + caret++; + } + + } else { + + if (toupper ((int) *caret) == 'U') { + + caret++; + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + } else if (toupper ((int) *caret) == 'L') { + + caret++; + + if (toupper ((int) *caret) == 'L') { + caret++; + } + + if (toupper ((int) *caret) == 'U') { + caret++; + } + + } + + } + + tok = xmalloc (sizeof (*tok)); + tok->kind = TOK_PPNUM; + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + tok->ident = xstrndup (start, (int) (caret - start)); + tok->len = (caret - start); + + vec_push (&vector_tokens, tok); + + if (is_name_part ((int) *caret)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, *line, caret, "unexpected '%c' character", *caret); + + while (is_name_part ((int) *caret)) { + caret++; + } + + } + + continue; + + } + + if (ispunct ((int) *caret)) { + + struct punct *punct; + + if (state->mode == CC_MODE_PREPROCESS) { + + caret++; + + if (state->ofp) { + fprintf (state->ofp, "%c", *start); + } + + continue; + + } + + tok = xmalloc (sizeof (*tok)); + + if ((punct = find_punct (caret))) { + + tok->kind = punct->kind; + + tok->ident = xstrdup (punct->name); + tok->len = strlen (punct->name); + + caret += tok->len; + + } else { + + tok->kind = *caret; + + tok->ident = xstrndup (caret, 1);; + tok->len = 1; + + caret++; + + } + + tok->start = (in_macro == 2) ? *line : (real_start ? real_start : *line); + tok->caret = (in_macro == 2) ? start : (real_caret ? real_caret : start); + + vec_push (&vector_tokens, tok); + continue; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_INTERNAL_ERROR, *line, caret, "Do we see this???"); + caret++; + + } + + *line = caret; + + if (!in_macro) { + + if (state->mode == CC_MODE_PREPROCESS) { + + if (state->ofp) { + fputc ('\n', state->ofp); + } + + return 0; + + } + + return &vector_tokens; + + } + + return 0; + +} + +static void hash_line (const char *filename, unsigned long line_number) { + + if (state->no_linemarkers) { + return; + } + + if (state->traditional_linemarkers) { + + if (line_number > 0) { + fprintf (state->ofp, "#line %lu \"%s\"\n", line_number, filename); + } + + } else { + + if (flags) { + + fprintf (state->ofp, "# %lu \"%s\" %d\n", line_number, filename, flags); + flags = 0; + + } else { + fprintf (state->ofp, "# %lu \"%s\"\n", line_number, filename); + } + + } + +} + +void preprocess_file (const char *ifile) { + + char *line, *line_end; + FILE *fp; + + char *start, *sname, *caret; + int need_hash = 0; + + unsigned long newlines; + unsigned long new_line_number; + + unsigned long consumed_extra; + unsigned long old_new_line_number; + + struct pp_pseudo_op_entry *poe; + void *load_line_internal_data = NULL; + + struct cond *cond; + int cond_idx; + + struct hashtab_name *key; + struct macro *m; + + unsigned long blanks = 1; + + if (!ifile || strcmp (ifile, "-") == 0) { + + set_filename (xstrdup ("")); + fp = stdin; + + } else { + + set_filename (xstrdup (ifile)); + + if (!(fp = fopen (ifile, "r"))) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to open '%s' for reading", ifile); + return; + + } + + } + + hash_line (get_filename (), 1); + + set_line_number (0); + new_line_number = 1; + + if ((key = find_macro ("__FILE__"))) { + + const char *filename = get_filename ();; + + if (filename && (m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (1 + strlen (filename) + 2); + sprintf (m->value, "\"%s\"", filename); + + } + + } + + load_line_internal_data = load_line_create_internal_data (&new_line_number); + + while (!load_line (&line, &line_end, 0, 0, &newlines, fp, &load_line_internal_data)) { + + set_line_number (new_line_number); + new_line_number += newlines + 1; + + consumed_extra = newlines; + + if ((key = find_macro ("__LINE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (16); + sprintf (m->value, "%lu", get_line_number ()); + + } + + } + + start = line; + caret = skip_whitespace (line); + + if (caret >= line_end) { + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + continue; + + } + + if (ignore_line && *caret != '#') { + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + continue; + + } + + while ((line = skip_whitespace (line)) < line_end) { + + caret = line; + + if (line[0] == '/' && line[1] == '/') { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "C++ style comments are not allowed in ISO C90"); + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + break; + + } + + if (*caret == '#') { + + caret = (line = skip_whitespace (caret + 1)); + + if (is_name_beginner ((int) *line)) { + + while (is_name_part ((int) *line)) { + line++; + } + + sname = xstrndup (caret, line - caret); + line = skip_whitespace (line); + + if ((poe = find_cond_directive (sname))) { + + ignore_line = poe->handler (start, &line); + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + free (sname); + continue; + + } + + if (!ignore_line) { + + if (strcmp (sname, "pragma") == 0) { + + caret = skip_whitespace (line); + free (sname); + + if (is_name_beginner ((int) *line)) { + + sname = symname (&line); + + if (strcmp (sname, "once") == 0) { + + free (sname); + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + if (includes) { + + if (is_pragma_igored (ifile)) { + break; + } + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "#pragma once in main file"); + } + + continue; + + } + + } + + caret = (line = skip_whitespace (start)); + goto handle_line; + + } + + if (strcmp (sname, "include") == 0) { + + int ret = handler_include (start, &line); + free (sname); + + if (flags == 2) { + + if (!state->no_linemarkers) { + + hash_line (get_filename (), new_line_number); + + need_hash = 0; + blanks = 1; + + } + + } else { + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + } + + if (ret) { + break; + } + + continue; + + } + + if ((poe = find_directive (sname))) { + + int ret = poe->handler (start, &line); + free (sname); + + if (!state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + if (ret) { + break; + } + + continue; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%s'", sname); + + } + + if (ignore_line && !state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + free (sname); + continue; + + } + + if (ignore_line && !state->no_linemarkers) { + + blanks += (newlines + 1); + need_hash = 1; + + } + + if (!is_end_of_line[(int) *caret] && !ignore_line) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%c'", *caret); + } + + continue; + + } + + handle_line: + + if (!ignore_line) { + + if (!state->no_linemarkers && need_hash) { + + if (blanks < 10) { + + while (blanks > 1) { + + putc ('\n', state->ofp); + blanks--; + + } + + } else { + + hash_line (get_filename (), get_line_number ()); + blanks = 1; + + } + + need_hash = 0; + + } + + blanks = 1; + + if (line < line_end) { + + if (line_has_unclosed_function_macro (start, line_end)) { + + old_new_line_number = new_line_number; + + start = collect_macro_continuation (start, &line_end, fp, &load_line_internal_data, &new_line_number); + consumed_extra += new_line_number - old_new_line_number; + + } + + preprocess_line (&start, 0, 0, 0); + + if (!state->no_linemarkers && consumed_extra) { + + blanks += consumed_extra; + need_hash = 1; + + } + + } + + } + + break; + + } + + } + +/*end:*/ + + load_line_destroy_internal_data (load_line_internal_data); + + if (!includes) { + + for (cond_idx = 0; cond_idx < vec_ifstack.length; cond_idx++) { + + cond = vec_ifstack.data[cond_idx]; + report_at (cond->filename, cond->line_number, REPORT_ERROR, "unterminated #%s statement", cond->directive); + + free (cond->filename); + free (cond->directive); + + free (cond); + + } + + } + + if (fp != stdin) { fclose (fp); } + +} diff --git a/pp.h b/pp.h new file mode 100755 index 0000000..80a385d --- /dev/null +++ b/pp.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * @file pp.h + *****************************************************************************/ +#ifndef _PP_H +#define _PP_H + +struct cond { + + char *directive, *filename; + + int ignore_line; + unsigned long line_number; + + int need_else; + int has_else; + +}; + +int preprocess_init (void); +void preprocess_file (const char *ifile); + +struct pp_pseudo_op_entry { + + const char *name; + int (*handler) (char *start, char **pp); + +}; + +extern int ignore_line; + +struct pp_pseudo_op_entry *find_cond_directive (char *name); +struct pp_pseudo_op_entry *find_directive (char *name); + +#include "vector.h" + +extern struct vector vec_ifstack; +extern struct vector vec_include_paths; + +struct vector *preprocess_line (char **line, char *real_start, char *real_caret, int in_macro); + +#endif /* _PP_H */ diff --git a/report.c b/report.c new file mode 100755 index 0000000..caea66a --- /dev/null +++ b/report.c @@ -0,0 +1,285 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "ll.h" +#include "report.h" + +extern char *xstrdup (const char *__p); +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +static int OriginalConsoleColor = -1; +#endif + +static void reset_console_color (void) { + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + + fprintf (stderr, "\033[0m"); + +#elif defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (OriginalConsoleColor == -1) { return; } + + SetConsoleTextAttribute (hStdError, OriginalConsoleColor); + OriginalConsoleColor = -1; + +#endif + +} + +static void set_console_color (int color) { + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + + fprintf (stderr, "\033[%dm", color); + +#elif defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + WORD wColor; + + if (OriginalConsoleColor == -1) { + + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) { + return; + } + + OriginalConsoleColor = csbi.wAttributes; + + } + + wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF); + SetConsoleTextAttribute (hStdError, wColor); + +#endif + +} +#endif + +extern int has_include_stack (void); +extern void print_include_stack (void); + +static void output_message (const char *filename, unsigned long lineno, unsigned long idx, int type, const char *fmt, va_list ap) { + + if (has_include_stack ()) { + print_include_stack (); + } + + if (filename) { + + if (lineno == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%lu: ", lineno); + } else { + fprintf (stderr, "%lu:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%lu: ", idx); + } + + if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_ERROR); +#endif + + if (type == REPORT_ERROR) { + fprintf (stderr, "error:"); + } else { + fprintf (stderr, "fatal error:"); + } + + } else if (type == REPORT_INTERNAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_INTERNAL_ERROR); +#endif + + fprintf (stderr, "internal error:"); + + } else if (type == REPORT_WARNING) { + +#ifndef __PDOS__ + set_console_color (COLOR_WARNING); +#endif + + fprintf (stderr, "warning:"); + + } + +#ifndef __PDOS__ + reset_console_color (); +#endif + + fprintf (stderr, " "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + + if (type != REPORT_WARNING) { + ++errors; + } + +} + +unsigned int get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned long lineno, int type, const char *fmt, ...) { + + va_list ap; + + va_start (ap, fmt); + output_message (filename, lineno, 0, type, fmt, ap); + va_end (ap); + +} + +static char *get_actual_line (const char *filename, unsigned long lineno) { + + char *line, *line_end, *ret; + void *load_line_internal_data; + + unsigned long newlines; + unsigned long new_line_number = 1; + + FILE *fp; + + if ((fp = fopen (filename, "r"))) { + + load_line_internal_data = load_line_create_internal_data (&new_line_number); + + while (!load_line (&line, &line_end, 0, 0, &newlines, fp, &load_line_internal_data)) { + + if (new_line_number == lineno) { + + if (line[line_end - line] == '\n') { + line[line_end - line] = '\0'; + } + + ret = xstrdup (line); + + load_line_destroy_internal_data (load_line_internal_data); + fclose (fp); + + return ret; + + } + + new_line_number += newlines + 1; + + } + + load_line_destroy_internal_data (load_line_internal_data); + fclose (fp); + + } + + return 0; + +} + +void report_line_at (const char *filename, unsigned long lineno, int type, const char *str, const char *caret, const char *fmt, ...) { + + unsigned long ident = 1, idx = 0, len, i; + va_list ap; + + char *actual_line = get_actual_line (filename, lineno); + + /*if (!(actual_line = get_actual_line (filename, lineno))) { + + filename = get_real_filename (); + lineno += (get_real_line_number () - lineno); + + }*/ + + if (str && caret) { idx = (caret - str) + 1; } + + va_start (ap, fmt); + output_message (filename, lineno, idx, type, fmt, ap); + va_end (ap); + + if (str && caret) { + + if (actual_line) { + + if (lineno > 0) { + ident = fprintf (stderr, " %8lu | ", lineno); + } else { + ident = fprintf (stderr, "%*s", 12, ""); + } + + fprintf (stderr, "%s", actual_line); + + if (actual_line[strlen (actual_line) - 1] != '\n') { + fprintf (stderr, "\n"); + } + + len = (idx + ident) - 1; + + for (i = 0; i < ident; i++) { + putc (' ', stderr); + } + + for (; i < len; i++) { + putc ('-', stderr); + } + + fprintf (stderr, "^\n"); + free (actual_line); + + }/* else { + + if (lineno > 0) { + ident = fprintf (stderr, " %8lu | ", lineno); + } else { + ident = fprintf (stderr, "%*s", 12, ""); + } + + fprintf (stderr, "%s", str); + + if (str[strlen (str) - 1] != '\n') { + fprintf (stderr, "\n"); + } + + len = (idx + ident) - 1; + + for (i = 0; i < ident; i++) { + putc (' ', stderr); + } + + for (; i < len; i++) { + putc ('-', stderr); + } + + fprintf (stderr, "^\n"); + + }*/ + + } + +} diff --git a/report.h b/report.h new file mode 100755 index 0000000..eb184b1 --- /dev/null +++ b/report.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +#define REPORT_WARNING 0 +#define REPORT_ERROR 1 +#define REPORT_FATAL_ERROR 3 +#define REPORT_INTERNAL_ERROR 4 + +unsigned int get_error_count (void); + +void report_at (const char *filename, unsigned long lineno, int type, const char *fmt, ...); +void report_line_at (const char *filename, unsigned long lineno, int type, const char *str, const char *caret, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/stdint.h b/stdint.h new file mode 100755 index 0000000..1b97ab8 --- /dev/null +++ b/stdint.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * @file stdint.h + *****************************************************************************/ +#ifndef _STDINT_H_INCLUDED +#ifndef _STDINT_H +#ifndef _STDINT_H_ + +#define _STDINT_H_INCLUDED +#define _STDINT_H +#define _STDINT_H_ + +#include + +/* Add all data types (even though we don't use them) as the project seems to fail to build on some systems. */ +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef signed short int16_t; +typedef unsigned short uint16_t; + +#if INT_MAX > 32767 +typedef signed int int32_t; +typedef unsigned int uint32_t; +#else +typedef signed long int32_t; +typedef unsigned long uint32_t; +#endif + +#ifndef _INT64_T +#define _INT64_T +#if defined (NO_LONG_LONG) || ((ULONG_MAX >> 16) >> 16) == 0xffffffff +typedef signed long int64_t; +#else +typedef signed long long int64_t; +#endif +#endif /* _INT64_T */ + +#ifndef _UINT64_T +#define _UINT64_T +#if defined (NO_LONG_LONG) || ((ULONG_MAX >> 16) >> 16) == 0xffffffff +typedef unsigned long uint64_t; +#else +typedef unsigned long long uint64_t; +#endif +#endif /* _UINT64_T */ + +#endif /* _STDINT_H_ */ +#endif /* _STDINT_H */ +#endif /* _STDINT_H_INCLUDED */ diff --git a/sym.c b/sym.c new file mode 100644 index 0000000..eab3bee --- /dev/null +++ b/sym.c @@ -0,0 +1,7 @@ +/****************************************************************************** + * @file sym.c + *****************************************************************************/ +#include "cc.h" + + + \ No newline at end of file diff --git a/token.c b/token.c new file mode 100755 index 0000000..8e8335e --- /dev/null +++ b/token.c @@ -0,0 +1,2195 @@ +/****************************************************************************** + * @file token.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "cc.h" +#include "cstr.h" +#include "hashtab.h" +#include "int64.h" +#include "lex.h" +#include "lib.h" +#include "ll.h" +#include "macro.h" +#include "pp.h" +#include "report.h" +#include "token.h" +#include "vector.h" + +static unsigned long new_line_number = 1, newlines = 0; +static char *start = 0, *line = 0; + +static char *line_end = 0; +static void *load_line_internal_data = 0; + +static struct vector vec_history = { 0 }; +static unsigned long real_new_line_number = 1; + +struct history { + + FILE *ifp; + + unsigned long new_line_number; + unsigned long newlines; + + const char *filename; + unsigned long line_number; + + char *start; + char *line; + + char *line_end; + void *load_line_internal_data; + + struct vector *vec_tokens; + unsigned long real_new_line_number; + +}; + +static struct vector *vec_tokens = 0; + +static int handler_include (char *start, char **pp) { + + char *caret, *sname, ch; + int i; + + char *inc_path, *tmp; + FILE *fp; + + char *current_directory,*p; + int len; + + struct history *history; + + if (**pp != '"' && **pp != '<') { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, *pp, "#include expects \"FILENAME\" or "); + return 1; + + } + + ch = (**pp == '"' ? '"' : '>'); + caret = (*pp)++; + + while (!is_end_of_line[(int) **pp]) { + + if (**pp == ch) { break; } + (*pp)++; + + } + + if (**pp != ch) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "#include expects \"FILENAME\" or "); + return 1; + + } else { + (*pp)++; + } + + sname = xstrndup (caret + 1, *pp - caret - 2); + + if (ch == '"') { + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + if (sname[0] == '/') { +#else + if ((isalpha ((int) sname[0]) && sname[1] == ':') || sname[0] == '/' || sname[0] == '\\') { +#endif + + if ((fp = fopen (sname, "r"))) { + + fclose (fp); + + history = xmalloc (sizeof (*history)); + history->ifp = state->ifp; + + history->new_line_number = new_line_number; + history->newlines = newlines; + + history->filename = get_filename (); + history->line_number = get_line_number (); + + history->start = start; + history->line = line; + + history->line_end = line_end; + history->load_line_internal_data = load_line_internal_data; + + history->vec_tokens = vec_tokens; + history->real_new_line_number = real_new_line_number; + + if (init_tokenizer (sname)) { + + free (history); + return 1; + + } + + vec_tokens = 0; + + vec_push (&vec_history, history); + return 0; + + } + + goto error; + + } else if ((p = strrchr (get_filename (), '/')) || (p = strrchr (get_filename (), '\\'))) { + + len = p - get_filename (); + + tmp = xmalloc (len + 1 + strlen (sname) + 1); + sprintf (tmp, "%.*s/%s", len, get_filename (), sname); + + if ((fp = fopen (tmp, "r"))) { + + fclose (fp); + + history = xmalloc (sizeof (*history)); + history->ifp = state->ifp; + + history->new_line_number = new_line_number; + history->newlines = newlines; + + history->filename = get_filename (); + history->line_number = get_line_number (); + + history->start = start; + history->line = line; + + history->line_end = line_end; + history->load_line_internal_data = load_line_internal_data; + + history->vec_tokens = vec_tokens; + history->real_new_line_number = real_new_line_number; + + if (init_tokenizer (tmp)) { + + free (history); + return 1; + + } + + vec_tokens = 0; + + vec_push (&vec_history, history); + return 0; + + } + + free (tmp); + + } + +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + if (get_filename ()[0] == '/') { +#else + if ((isalpha ((int) get_filename ()[0]) && get_filename ()[1] == ':') || get_filename ()[0] == '/' || get_filename ()[0] == '\\') { +#endif + + if ((current_directory = get_current_directory ())) { + + if (strcmp (current_directory, "") == 0) { + + tmp = xmalloc (strlen (sname) + 1); + sprintf (tmp, "%s", sname); + + } else { + + tmp = xmalloc (strlen (current_directory) + 1 + strlen (sname) + 1); + sprintf (tmp, "%s/%s", current_directory, sname); + + } + + free (current_directory); + + if ((fp = fopen (tmp, "r"))) { + + fclose (fp); + + history = xmalloc (sizeof (*history)); + history->ifp = state->ifp; + + history->new_line_number = new_line_number; + history->newlines = newlines; + + history->filename = get_filename (); + history->line_number = get_line_number (); + + history->start = start; + history->line = line; + + history->line_end = line_end; + history->load_line_internal_data = load_line_internal_data; + + history->vec_tokens = vec_tokens; + history->real_new_line_number = real_new_line_number; + + if (init_tokenizer (tmp)) { + + free (history); + return 1; + + } + + vec_tokens = 0; + + vec_push (&vec_history, history); + return 0; + + } + + free (tmp); + + } + + } else { + + if ((fp = fopen (sname, "r"))) { + + history = xmalloc (sizeof (*history)); + history->ifp = state->ifp; + + history->new_line_number = new_line_number; + history->newlines = newlines; + + history->filename = get_filename (); + history->line_number = get_line_number (); + + history->start = start; + history->line = line; + + history->line_end = line_end; + history->load_line_internal_data = load_line_internal_data; + + history->vec_tokens = vec_tokens; + history->real_new_line_number = real_new_line_number; + + if (init_tokenizer (sname)) { + + free (history); + return 1; + + } + + vec_tokens = 0; + + vec_push (&vec_history, history); + return 0; + + } + + } + + } + + if (vec_include_paths.length > 0) { + + for (i = 0; i < vec_include_paths.length; i++) { + + inc_path = vec_include_paths.data[i]; + + tmp = xmalloc (strlen (inc_path) + strlen (sname) + 1); + sprintf (tmp, "%s%s", inc_path, sname); + + if ((fp = fopen (tmp, "r"))) { + + fclose (fp); + + history = xmalloc (sizeof (*history)); + history->ifp = state->ifp; + + history->new_line_number = new_line_number; + history->newlines = newlines; + + history->filename = get_filename (); + history->line_number = get_line_number (); + + history->start = start; + history->line = line; + + history->line_end = line_end; + history->load_line_internal_data = load_line_internal_data; + + history->vec_tokens = vec_tokens; + history->real_new_line_number = real_new_line_number; + + if (init_tokenizer (tmp)) { + + free (history); + return 1; + + } + + vec_tokens = 0; + + vec_push (&vec_history, history); + return 0; + + } + + free (tmp); + + } + + } + +error: + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "failed to open '%s' for reading", sname); + return 0; + +} + +static int get_line (void) { + + const char *filename; + unsigned long line_number = 0, flags, ln; + + char *saved_line, *caret, *temp, *name; + int ch, ret; + + struct pp_pseudo_op_entry *poe; + struct hashtab_name *key; + struct macro *m; + + if (!load_line_internal_data || !state->ifp) { + return 0; + } + + while (!load_line (&line, &line_end, 0, 0, &newlines, state->ifp, &load_line_internal_data)) { + + set_real_line_number (real_new_line_number); + set_line_number (new_line_number); + + if ((key = find_macro ("__LINE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (16); + sprintf (m->value, "%lu", new_line_number); + + } + + } + + real_new_line_number += newlines + 1; + new_line_number += newlines + 1; + + start = saved_line = line; + + while (line < line_end) { + + caret = (line = skip_whitespace (line)); + + if (line[0] == '/' && line[1] == '/') { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "C++ style comments are not allowed in ISO C90"); + break; + + } + + if (*line == '#') { + + caret = (line = skip_whitespace (line + 1)); + + if (is_name_beginner ((int) *line)) { + + name = symname (&line); + + if (strcmp (name, "line") == 0) { + + caret = (line = skip_whitespace (line)); + free (name); + + if (!isdigit ((int) *line)) { + + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected line number"); + continue; + + } + + line_number = 0; + + while (isdigit ((int) *line)) { + + line_number = line_number * 10 + (*line - '0'); + line++; + + } + + caret = (line = skip_whitespace (line)); + + if (*line != '"') { + + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected filename"); + continue; + + } + + ch = *line++; + + while (!is_end_of_line[(int) *line]) { + + if (*line == '\\') { + + line++; + + if (*line == ch) { + (line)++; + } + + continue; + + } + + if (*line == ch) { break; } + (line)++; + + } + + if (*line != ch) { + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "missing terminating %c character", ch); + + temp = xmalloc (line - caret); + sprintf (temp, "%.*s", (int) (line - caret), caret + 1); + + new_line_number = line_number; + set_filename (temp); + + } else { + + new_line_number = line_number; + set_filename (xstrndup (caret + 1, line - caret - 1)); + + line++; + + } + + caret = skip_whitespace (line); + + if (!is_end_of_line[(int) *caret]) { + report_line_at (get_real_filename (), get_real_line_number (), REPORT_WARNING, start, caret, "extra tokens at end of #line directive"); + } + + continue; + + } + + line = skip_whitespace (line); + + if ((poe = find_cond_directive (name))) { + + ignore_line = poe->handler (start, &line); + + free (name); + break; + + } + + if (!ignore_line) { + + if (strcmp (name, "pragma") == 0) { + + caret = skip_whitespace (line); + free (name); + + if (is_name_beginner ((int) *line)) { + + name = symname (&line); + + if (strcmp (name, "once") == 0) { + + free (name); + + if (vec_history.length > 0) { + + if (is_pragma_igored (get_real_filename ())) { + goto cleanup; + } + + } else { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "#pragma once in main file"); + } + + continue; + + } + + } + + caret = (line = skip_whitespace (start)); + break; + + } + + if (strcmp (name, "include") == 0) { + + ret = handler_include (start, &line); + free (name); + + if (ret) { + break; + } + + continue; + + } + + if ((poe = find_directive (name))) { + + ret = poe->handler (start, &line); + free (name); + + if (ret) { + break; + } + + continue; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "unknown preprocessor directive '#%s'", name); + + } + + free (name); + + while (!is_end_of_line[(int) *line]) { + line++; + } + + continue; + + } + + if (!isdigit ((int) *line)) { + + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected line number"); + continue; + + } + + line_number = 0; + + while (isdigit ((int) *line)) { + + line_number = line_number * 10 + (*line - '0'); + line++; + + } + + caret = (line = skip_whitespace (line)); + + if (*line != '"') { + + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected filename"); + continue; + + } + + ch = *line++; + + while (!is_end_of_line[(int) *line]) { + + if (*line == '\\') { + + line++; + + if (*line == ch) { + (line)++; + } + + continue; + + } + + if (*line == ch) { break; } + (line)++; + + } + + filename = get_filename (); + ln = get_line_number (); + + if (*line != ch) { + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "missing terminating %c character", ch); + + temp = xmalloc (line - caret); + sprintf (temp, "%.*s", (int) (line - caret), caret + 1); + + new_line_number = line_number; + set_filename (temp); + + } else { + + new_line_number = line_number; + set_filename (xstrndup (caret + 1, line - caret - 1)); + + line++; + + } + + caret = (line = skip_whitespace (line)); + + if (!is_end_of_line[(int) *line]) { + + if (!isdigit ((int) *line)) { + + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "expected flags"); + continue; + + } + + flags = 0; + + while (isdigit ((int) *line)) { + + flags = flags * 10 + (*line - '0'); + line++; + + } + + switch (flags) { + + case 1: { + + struct hist_entry *entry = xmalloc (sizeof (*entry)); + + entry->filename = filename; + entry->line_number = ln; + + vec_push (&state->vec_hist, entry); + break; + + } + + case 2: { + + struct hist_entry *entry; + + if (state->vec_hist.length == 0) { + report_line_at (get_real_filename (), get_real_line_number (), REPORT_ERROR, start, caret, "invalid line marker flag '%d': cannot pop empty include stack", flags); + } else { + + entry = vec_pop (&state->vec_hist); + + if (strcmp (entry->filename, get_filename ())) { + + report_at (get_real_filename (), get_real_line_number (), REPORT_WARNING, "file \"%s\" linemaker ignored due to incorrect nesting", get_filename ()); + + entry->line_number += newlines; + entry->line_number++; + + new_line_number = entry->line_number; + set_filename (entry->filename); + + /*state->corrupted = 1;*/ + + } + + free (entry); + + } + + break; + + } + + default: + + break; + + } + + } + + continue; + + } + + if (!ignore_line) { + + line = saved_line; + return 1; + + } + + break; + + } + + } + +cleanup: + + load_line_destroy_internal_data (load_line_internal_data); + load_line_internal_data = 0; + + if (vec_history.length == 0) { + + struct cond *cond; + long i; + + for (i = 0; i < vec_ifstack.length; i++) { + + cond = vec_ifstack.data[i]; + report_at (cond->filename, cond->line_number, REPORT_ERROR, "unterminated #%s statement", cond->directive); + + free (cond->filename); + free (cond->directive); + + free (cond); + + } + + } + + return 0; + +} + +static int chrpos (char *s, int ch) { + + char *p = strchr (s, ch); + return p ? p - s : -1; + +} + +#include "stdint.h" + +static int hex_char (uint64_t *out, struct token *tok, int idx) { + + const char *s = tok->ident + idx + 1; + + int f = 0, i = 0, h; + uint64_t n = 0; + + while (*s && isxdigit ((int) *s)) { + + h = chrpos ("0123456789abcdef", tolower ((int) *s)); + + f = 1; + i++; + + n = n * 16 + h; + s++; + + } + + if (!f) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + idx - 1, "\\x used with no following hex digits"); + return i; + + } + + if (n > ((1U << 8) - 1)) { + + uint64_t val = n % ((1U << 8) - 1); + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, tok->start, tok->caret + idx - 1, "hex escape sequence out of range"); + *out = *out * (1U << 8) + val; + + } else { + *out = *out * (1U << 8) + n; + } + + return i; + +} + +static void scan_ch (struct token *tok, int is_long) { + + const char *s = tok->ident; + int has_warned = 0, i; + + int64_s i64; + uint64_t n = 0; + + if (is_long) { + s++; + } + + s++; + + while (*s) { + + if (*s == '\\') { + + switch (*(++s)) { + + case 'a': + + n = '\a'; + break; + + case 'b': + + n = '\b'; + break; + + case 'f': + + n = '\f'; + break; + + case 'n': + + n = '\n'; + break; + + case 'r': + + n = '\r'; + break; + + case 't': + + n = '\t'; + break; + + case 'v': + + n = '\v'; + break; + + case '\\': + + n = '\\'; + break; + + case '"': + + n = '"' | (1U << 8); + break; + + case '\'': + + n = '\''; + break; + + case '0': case '1': + case '2': case '3': + case '4': case '5': + case '6': case '7': + + for (i = 0; isdigit ((int) *s) && *s < '8'; s++) { + + if (++i > 3) { + break; + } + + n = n * 8 + (*s - '0'); + + } + + s--; + break; + + case 'x': + + s += hex_char (&n, tok, s - tok->ident); + break; + + default: + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + (s - tok->ident), "unknown escape sequence"); + + n = ' '; + break; + + } + + } else { + n = *s; + } + + zext64 (&i64, 1U << (is_long ? 16 : 8)); + mul64 (&tok->val.i, i64); + + zext64 (&i64, n & ((1U << (is_long ? 16 : 8)) - 1)); + add64 (&tok->val.i, i64); + + if (*(++s) == '\'') { + break; + } + + if (!has_warned) { + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, tok->start, tok->caret, "multi-character character constant"); + has_warned = 1; + + } + + } + +} + +/*static void scan_int (struct token *tok) { + + const char *s = tok->ident; + int radix = 10, k; + + if (*s == '0') { + + radix = 8; + s++; + + if (*s == 'x') { + + radix = 16; + s++; + + } + + } + + while (*s != '\0' && (k = chrpos ("0123456789abcdef", tolower ((int) *s))) >= 0) { + + if (k >= radix) { + + int idx = s - tok->ident; + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret + idx, "invalid digit in integer literal"); + + } + + tok->val.d = tok->val.d * radix + k; + s++; + + } + +}*/ + + +int tok_ident = TOK_IDENT; + +struct keyword { + + char *name; + + int std; + long verison; + + enum token_kind kind; + +}; + +struct punct *find_punct (char *p) { + + static struct punct kws[] = { + + { "<<=", TOK_LSHEQ }, + { ">>=", TOK_RSHEQ }, + { "...", TOK_ELLIPSIS }, + + { "!=", TOK_NOTEQ }, + { "%=", TOK_MODEQ }, + { "&&", TOK_LOGAND }, + { "&=", TOK_ANDEQ }, + { "*=", TOK_STAREQ }, + { "++", TOK_INCR }, + { "+=", TOK_PLUSEQ }, + { "--", TOK_DECR }, + { "-=", TOK_MINUSEQ }, + { "->", TOK_ARROW }, + { "/=", TOK_SLASHEQ }, + { "<<", TOK_LSH }, + { "<=", TOK_LTEQ }, + { "==", TOK_EQEQ }, + { ">>", TOK_RSH }, + { ">=", TOK_GTEQ }, + { "^=", TOK_XOREQ }, + { "||", TOK_LOGOR }, + { "|=", TOK_OREQ }, + + { "!", TOK_XMARK }, + { "%", TOK_MOD }, + { "&", TOK_AMPER }, + { "(", TOK_LPAREN }, + { ")", TOK_RPAREN }, + { "*", TOK_STAR }, + { "+", TOK_PLUS }, + { ",", TOK_COMMA }, + { "-", TOK_MINUS }, + { ".", TOK_DOT }, + { "/", TOK_BSLASH }, + { ":", TOK_COLON }, + { ";", TOK_SEMI }, + { "<", TOK_LESS }, + { "=", TOK_ASSIGN }, + { ">", TOK_GREATER }, + { "?", TOK_QMARK }, + { "[", TOK_LBRACK }, + { "]", TOK_RBRACK }, + { "^", TOK_CARET }, + { "{", TOK_LBRACE }, + { "|", TOK_PIPE }, + { "}", TOK_RBRACE }, + { "~", TOK_TILDE } + + }; + + struct punct *kw; + unsigned long i; + + for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) { + + kw = &kws[i]; + + if (strncmp (p, kw->name, strlen (kw->name)) == 0) { + return kw; + } + + } + + return 0; + +} + +#ifndef VERSION +# define VERSION 0L +#endif + +static int find_kind (const char *start, const char *caret, const char *p) { + + static struct keyword kws[] = { + + /* Compiler Specific keywords */ + { "__asm__", 0, VERSION, TOK_ASM }, + { "__inline__", 0, VERSION, TOK_INLINE }, + { "__restrict__", 0, VERSION, TOK_RESTRICT }, + + /* C90 keywords */ + { "auto", 90, VERSION, TOK_AUTO }, + { "break", 90, VERSION, TOK_BREAK }, + { "case", 90, VERSION, TOK_CASE }, + { "char", 90, VERSION, TOK_CHAR }, + { "const", 90, VERSION, TOK_CONST }, + { "continue", 90, VERSION, TOK_CONTINUE }, + { "default", 90, VERSION, TOK_DEFAULT }, + { "do", 90, VERSION, TOK_DO }, + { "double", 90, VERSION, TOK_DOUBLE }, + { "else", 90, VERSION, TOK_ELSE }, + { "enum", 90, VERSION, TOK_ENUM }, + { "extern", 90, VERSION, TOK_EXTERN }, + { "float", 90, VERSION, TOK_FLOAT }, + { "for", 90, VERSION, TOK_FOR }, + { "goto", 90, VERSION, TOK_GOTO }, + { "if", 90, VERSION, TOK_IF }, + { "int", 90, VERSION, TOK_INT }, + { "long", 90, VERSION, TOK_LONG }, + { "register", 90, VERSION, TOK_REGISTER }, + { "return", 90, VERSION, TOK_RETURN }, + { "short", 90, VERSION, TOK_SHORT }, + { "signed", 90, VERSION, TOK_SIGNED }, + { "sizeof", 90, VERSION, TOK_SIZEOF }, + { "static", 90, VERSION, TOK_STATIC }, + { "struct", 90, VERSION, TOK_STRUCT }, + { "switch", 90, VERSION, TOK_SWITCH }, + { "typedef", 90, VERSION, TOK_TYPEDEF }, + { "union", 90, VERSION, TOK_UNION }, + { "unsigned", 90, VERSION, TOK_UNSIGNED }, + { "void", 90, VERSION, TOK_VOID }, + { "volatile", 90, VERSION, TOK_VOLATILE }, + { "while", 90, VERSION, TOK_WHILE }, + + /* C99 keywords */ + { "inline", 99, VERSION, TOK_INLINE }, + { "restrict", 99, VERSION, TOK_RESTRICT } + + }; + + struct keyword *kw; + unsigned long i; + + for (i = 0; i < (sizeof (kws) / sizeof (*kws)); i++) { + + kw = &kws[i]; + + if (strcmp (p, kw->name) == 0 && kw->verison <= state->version) { + + if (state->std) { + + if (state->pedantic) { + + if (!kw->std || kw->std > state->std) { + break; + } + + } + + if (kw->std) { + + if (kw->std > state->std) { + break; + } + + return kw->kind; + + } + + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, start, caret, "'%s' is a compiler-specific keyword so compatiblity isn't guarenteed", kw->name); + + } + + return kw->kind; + + } + + } + + return TOK_IDENT; + +} + +#define BN_SIZE 4 + +/* bn = (bn << shift) | or_val */ +static int bn_lshift (unsigned int *bn, int shift, int or_val) { + + int i; + unsigned int v; + + if (bn[BN_SIZE - 1] >> (32 - shift)) { + return shift; + } + + for (i = 0; i < BN_SIZE; i++) { + + v = bn[i]; + + bn[i] = (v << shift) | or_val; + or_val = v >> (32 - shift); + + } + + return 0; + +} + +float strtof (const char *nptr, char **endptr) { + + const char *s = nptr; + float res = 0.0f; + + int sign = 1; + int exp = 0; + int has_digits = 0; + + while (isspace ((unsigned char) *s)) { + s++; + } + + if (*s == '-') { + + sign = -1; + s++; + + } else if (*s == '+') { + s++; + } + + /* Integer part */ + while (isdigit ((unsigned char) *s)) { + + res = res * 10.0f + (*s++ - '0'); + has_digits = 1; + + } + + /* Fractional part */ + if (*s == '.') { + + float factor = 0.1f; + s++; + + while (isdigit ((unsigned char) *s)) { + + res += (*s++ - '0') * factor; + factor *= 0.1f; + + has_digits = 1; + + } + + } + + if (!has_digits) { + + if (endptr) { + *endptr = (char *) nptr; + } + + return 0.0f; + + } + + /* Exponent part */ + if (*s == 'e' || *s == 'E') { + + const char *backtrack = s; + + int exp_sign = 1; + int e_val = 0; + + s++; + + if (*s == '-') { + + exp_sign = -1; + s++; + + } else if (*s == '+') { + s++; + } + + if (isdigit ((unsigned char) *s)) { + + while (isdigit ((unsigned char) *s)) { + + e_val = e_val * 10 + (*s - '0'); + s++; + + } + + exp = e_val * exp_sign; + + } else { + s = backtrack; + } + + } + + if (exp != 0) { + + float base = (exp > 0) ? 10.0f : 0.1f; + int abs_exp = (exp > 0) ? exp : -exp; + + for (; abs_exp > 0; abs_exp--) { + res *= base; + } + + } + + if (endptr) { + *endptr = (char *) s; + } + + return res * sign; + +} + +long double strtold (const char *nptr, char **endptr) { + + const char *s = nptr; + + long double res = 0.0L; + int sign = 1, exp = 0, has_digits = 0; + + while (isspace ((unsigned char) *s)) { + s++; + } + + if (*s == '-') { + + sign = -1; + s++; + + } else if (*s == '+') { + s++; + } + + while (isdigit ((unsigned char) *s)) { + + res = res * 10.0L + (*s++ - '0'); + has_digits = 1; + + } + + if (*s == '.') { + + long double factor = 0.1L; + s++; + + while (isdigit ((unsigned char) *s)) { + + res += (long double) (*s++ - '0') * factor; + factor *= 0.1L; + + has_digits = 1; + + } + + } + + if (!has_digits) { + + if (endptr) { + *endptr = (char *) nptr; + } + + return 0.0L; + + } + + if (*s == 'e' || *s == 'E') { + + const char *back = s; + int esign = 1, eval = 0; + + s++; + + if (*s == '-') { + + esign = -1; + s++; + + } else if (*s == '+') { + s++; + } + + if (isdigit ((unsigned char) *s)) { + + while (isdigit ((unsigned char) *s)) { + eval = eval * 10 + (*s++ - '0'); + } + + exp = eval * esign; + + } else { + s = back; + } + + } + + /* Apply exponent in safe chunks to avoid intermediate overflow/underflow */ + if (exp != 0) { + + int abs_exp = (exp > 0) ? exp : -exp; + + while (abs_exp > 0) { + + int chunk = (abs_exp > 4000) ? 4000 : abs_exp; + + long double multiplier = 1.0L; + long double base = 10.0L; + + int i = chunk; + + /* Binary Exponentiation for the current chunk */ + while (i > 0) { + + if (i & 1) { + multiplier *= base; + } + + base *= base; + i >>= 1; + + } + + if (exp > 0) { + res *= multiplier; + } else { + res /= multiplier; + } + + abs_exp -= chunk; + + } + + } + + if (endptr) { + *endptr = (char *) s; + } + + return res * sign; + +} + +static void parse_number (struct token *tok) { + + const char *p = tok->ident, *q; + + unsigned int bn[BN_SIZE] = { 0 }; + long double d; + + CString cstr; + int b, t, shift, frac_bits, s, exp_val, ch; + + cstr_new (&cstr); + + t = (ch = *p++); + ch = *p++; + + cstr_ccat (&cstr, t); + b = 10; + + if (t == '0') { + + if (tolower ((int) ch) == 'x') { + + cstr.size--; + + ch = *p++; + b = 16; + + } + + } + + for (;;) { + + if (ch >= 'a' && ch <= 'f') { + t = ch - 'a' + 10; + } else if (ch >= 'A' && ch <= 'F') { + t = ch - 'A' + 10; + } else if (isdigit ((int) ch)) { + t = ch - '0'; + } else { + break; + } + + if (t >= b) { + break; + } + + cstr_ccat (&cstr, ch); + ch = *p++; + + } + + if (ch == '.' || ((ch == 'e' || ch == 'E') && b == 10) || ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { + + if (b != 10) { + + frac_bits = 0; + cstr_ccat (&cstr, '\0'); + + if (b == 16) { + shift = 4; + } else { + shift = 1; + } + + q = cstr.data; + + for (;;) { + + t = *q++; + + if (t == '\0') { + break; + } else if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + } + + frac_bits -= bn_lshift (bn, shift, t); + + } + + if (ch == '.') { + + ch = *p++; + + for (;;) { + + t = ch; + + if (t >= 'a' && t <= 'f') { + t = t - 'a' + 10; + } else if (t >= 'A' && t <= 'F') { + t = t - 'A' + 10; + } else if (t >= '0' && t <= '9') { + t = t - '0'; + } else { + break; + } + + if (t >= b) { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "invalid digit"); + } + + frac_bits -= bn_lshift (bn, shift, t); + frac_bits += shift; + + ch = *p++; + + } + + } + + if (ch != 'p' && ch != 'P') { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent"); + } + + ch = *p++; + + s = 1; + exp_val = 0; + + if (ch == '+') { + ch = *p++; + } else if (ch == '-') { + + s = -1; + ch = *p++; + + } + + if (ch < '0' || ch > '9') { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent digits"); + } + + while (ch >= '0' && ch <= '9') { + + if (exp_val < 100000000) { + exp_val = exp_val * 10 + ch - '0'; + } + + ch = *p++; + + } + + exp_val = exp_val * s; + + d = (long double) bn[3] * 79228162514264337593543950336.0L + (long double) bn[2] * 18446744073709551616.0L + (long double) bn[1] * 4294967296.0L + (long double) bn[0]; + d = ldexp (d, exp_val - frac_bits); + + t = toupper ((int) ch); + + if (t == 'F') { + + ch = *p++; + + tok->kind = TOK_CFLOAT; + tok->val.f = (float) d; + + } else if (t == 'L') { + + ch = *p++; + + tok->kind = TOK_CLDOUBLE; + tok->val.ld = d; + + } else { + + tok->kind = TOK_CDOUBLE; + tok->val.d = (double) d; + + } + + } else { + + if (ch == '.') { + + cstr_ccat (&cstr, ch); + ch = *p++; + + while (ch >= '0' && ch <= '9') { + + cstr_ccat (&cstr, ch); + ch = *p++; + + } + + } + + if (ch == 'e' || ch == 'E') { + + cstr_ccat (&cstr, ch); + ch = *p++; + + if (ch == '-' || ch == '+') { + + cstr_ccat (&cstr, ch); + ch = *p++; + + } + + if (ch < '0' || ch >= '9') { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "expected exponent digits"); + } + + while (ch >= '0' && ch <= '9') { + + cstr_ccat (&cstr, ch); + ch = *p++; + + } + + } + + cstr_ccat (&cstr, '\0'); + + t = toupper ((int) ch); + errno = 0; + + if (t == 'F') { + + ch = *p++; + + tok->kind = TOK_CFLOAT; + tok->val.f = strtof (cstr.data, 0); + + } else if (t == 'L') { + + ch = *p++; + + tok->kind = TOK_CLDOUBLE; + tok->val.ld = strtold (cstr.data, 0); + + } else { + + tok->kind = TOK_CDOUBLE; + tok->val.d = strtod (cstr.data, 0); + + } + + } + + } else { + + int64_s n = { 0 }, n1 = { 0 }, n2, n3, n4, n5; + + const char *p1; + int ov = 0, lcount, ucount; + + cstr_ccat (&cstr, '\0'); + q = cstr.data; + + if (b == 10 && *q == '0') { + + b = 8; + q++; + + } + + for (;;) { + + t = *q++; + + if (t == '\0') { + break; + } + + if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + } + + if (t >= b) { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "invalid digit"); + } + + zext64 (&n2, b); + zext64 (&n3, t); + + n1 = n; + + mul64 (&n, n2); + add64 (&n, n3); + + parse_string_to_i64 (&n4, "0x1000000000000000"); + n5 = n; + + if (gte64_unsigned (n1, n4)) { + + div64 (&n5, n2); + + if (neq64 (n5, n1)) { + ov = 1; + } + + } + + } + + lcount = ucount = 0; + p1 = p; + + for (;;) { + + t = toupper ((int) ch); + + if (t == 'L') { + + if (lcount >= 2) { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "three 'L's in integer constant"); + } + + if (lcount && *(p - 1) != ch) { + report_at (get_filename (), get_line_number (), REPORT_ERROR, "incorrect integer suffix: %s", p1); + } + + lcount++; + ch = *p++; + + } else if (t == 'U') { + + if (ucount >= 1) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok->start, tok->caret, "two 'U's in integer constant"); + } + + ucount++; + ch = *p++; + + } else { + break; + } + + } + + /*if (pp_expr) { + lcount = 2; + }*/ + + if (ucount == 0 && b == 10) { + + parse_string_to_i64 (&n4, "0x80000000"); + + if (gte64_unsigned (n, n4)) { + lcount = 1 + 1; + } + + parse_string_to_i64 (&n4, " 0x8000000000000000"); + + if (gte64_unsigned (n, n4)) { + ov = 1, ucount = 1; + } + + } else { + + parse_string_to_i64 (&n4, "0x100000000"); + parse_string_to_i64 (&n5, "0x80000000"); + + if (gte64_unsigned (n, n4)) { + lcount = 1 + 1; + } else if (gte64_unsigned (n, n5)) { + ucount = 1; + } + + parse_string_to_i64 (&n4, " 0x8000000000000000"); + + if (gte64_unsigned (n, n4)) { + ucount = 1; + } + + } + + if (ov) { + report_at (get_filename (), get_line_number (), REPORT_WARNING, "integer constant overflow"); + } + + tok->kind = TOK_CINT; + + if (lcount) { + + tok->kind = (state->long64 ? TOK_CLLONG : TOK_CLONG); + + if (lcount == 2) { + + if (state->std == 90) { + + const char *caret = tok->caret; + caret += (p1 - tok->ident); + + report_line_at (get_filename (), get_line_number (), state->pedantic ? REPORT_ERROR : REPORT_WARNING, tok->start, caret - 1, "use of C99 long long integer constant"); + + } + + tok->kind = TOK_CLLONG; + + } + + } + + if (ucount) { + tok->kind++; + } + + tok->val.i = n; + + } + +} + +static int tok_index = 0; +struct token tok = { 0 }; + +void unget_token (struct token *saved_tok) { + + if (tok_index > 0) { + tok_index--; + } + + tok.val = saved_tok->val; + tok.kind = saved_tok->kind; + + tok.start = saved_tok->start; + tok.caret = saved_tok->caret; + + tok.ident = saved_tok->ident; + tok.len = saved_tok->len; + + tok.is_unget = 1; + free (saved_tok); + +} + +static void token_skip_pp_quoted (char **pp, char *end) { + + char quote = **pp; + (*pp)++; + + while (*pp < end) { + + if (**pp == '\\') { + + (*pp)++; + + if (*pp < end) { + (*pp)++; + } + + continue; + + } + + if (**pp == quote) { + + (*pp)++; + break; + + } + + (*pp)++; + + } + +} + +static int token_line_has_unclosed_function_macro (char *line_start, char *end) { + + char *p = line_start, *q, *r, *sname; + int depth; + + struct hashtab_name *key; + struct macro *m; + + while (p < end) { + + if (*p == '"' || *p == '\'') { + + token_skip_pp_quoted (&p, end); + continue; + + } + + if (!is_name_beginner ((int) *p)) { + + p++; + continue; + + } + + q = p; + sname = symname (&q); + + if ((key = find_macro (sname)) && (m = get_macro (key)) && m->nargs >= 0) { + + r = skip_whitespace (q); + + if (r < end && *r == '(') { + + depth = 0; + + while (r < end) { + + if (*r == '"' || *r == '\'') { + + token_skip_pp_quoted (&r, end); + continue; + + } + + if (*r == '(') { + depth++; + } else if (*r == ')') { + + depth--; + + if (depth == 0) { + break; + } + + } + + r++; + + } + + free (sname); + + if (depth > 0) { + return 1; + } + + p = r; + continue; + + } + + } + + free (sname); + p = q; + + } + + return 0; + +} + +static char *token_collect_macro_continuation (char *line_start, char **line_end_p) { + + char *buf, *next, *next_end; + unsigned long len, next_len, cap, extra_newlines; + + len = *line_end_p - line_start; + cap = len + 1; + + buf = xmalloc (cap + 1); + memcpy (buf, line_start, len); + buf[len] = '\0'; + + while (token_line_has_unclosed_function_macro (buf, buf + len)) { + + if (load_line (&next, &next_end, 0, 0, &extra_newlines, state->ifp, &load_line_internal_data)) { + break; + } + + next_len = next_end - next; + + if (len + 1 + next_len + 1 > cap) { + + cap = len + 1 + next_len + 1 + 256; + buf = xrealloc (buf, cap + 1); + + } + + buf[len++] = ' '; + + memcpy (buf + len, next, next_len); + len += next_len; + + buf[len] = '\0'; + new_line_number += extra_newlines + 1; + + } + + *line_end_p = buf + len; + return buf; + +} + +void get_token (void) { + + struct history *history; + + struct hashtab_name *key; + struct macro *m; + + if (tok.is_unget) { + + if (tok.start) { + free ((char *) tok.start); + } + + } + + if (tok.ident) { free (tok.ident); } + memset (&tok, 0, sizeof (tok)); + + for (;;) { + + if (!line || skip_whitespace (line) >= line_end) { + + if (vec_tokens) { + + struct token *tok; + + if (tok_index < vec_tokens->length) { + break; + } + + while ((tok = vec_pop (vec_tokens))) { + free (tok); + } + + } + + if (!get_line ()) { + + free ((char *) get_filename ()); + + if ((history = vec_pop (&vec_history))) { + + state->ifp = history->ifp; + + new_line_number = history->new_line_number; + newlines = history->newlines; + + start = history->start; + line = history->line; + + line_end = history->line_end; + load_line_internal_data = history->load_line_internal_data; + + vec_tokens = history->vec_tokens; + real_new_line_number = history->real_new_line_number; + + set_filename_and_line_number (history->filename, history->line_number); + + if ((key = find_macro ("__FILE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (1 + strlen (history->filename) + 2); + sprintf (m->value, "\"%s\"", history->filename); + + } + + } + + if ((key = find_macro ("__LINE__"))) { + + if ((m = get_macro (key)) && m->type == MACRO_BUILTIN) { + + free (m->value); + + m->value = xmalloc (16); + sprintf (m->value, "%lu", history->line_number); + + } + + } + + free (history); + continue; + + } + + return; + + } + + continue; + + } + + if (token_line_has_unclosed_function_macro (start, line_end)) { + + start = token_collect_macro_continuation (start, &line_end); + line = start; + + } + + vec_tokens = preprocess_line (&line, 0, 0, 0); + + tok_index = 0; + break; + + } + + if (tok_index < vec_tokens->length) { + + memcpy (&tok, vec_tokens->data[tok_index++], sizeof (tok)); + + if (tok.kind == TOK_LCHAR || tok.kind == TOK_CCHAR) { + + scan_ch (&tok, tok.kind == TOK_LCHAR); + return; + + } + + if (tok.kind == TOK_PPNUM) { + + parse_number (&tok);; + return; + + } + + if (tok.kind == TOK_IDENT) { + + tok.kind = find_kind (tok.start, tok.caret, tok.ident);; + return; + + } + + } + +} + +int init_tokenizer (const char *ifile) { + + const char *tmp; + + if (!ifile || strcmp (ifile, "-") == 0) { + + tmp = xstrdup (""); + + set_real_filename (tmp); + set_filename (tmp); + + state->ifp = stdin; + + } else { + + tmp = xstrdup (ifile); + + set_real_filename (tmp); + set_filename (tmp); + + if (!(state->ifp = fopen (ifile, "r"))) { + + report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to open '%s' for reading", ifile); + return 1; + + } + + } + + set_real_line_number (0); + set_line_number (0); + + new_line_number = 1, newlines = 0; + start = 0, line = 0; + + line_end = 0; + real_new_line_number = 1; + + load_line_internal_data = load_line_create_internal_data (&new_line_number); + return 0; + +} diff --git a/token.h b/token.h new file mode 100755 index 0000000..cbf67e4 --- /dev/null +++ b/token.h @@ -0,0 +1,171 @@ +/****************************************************************************** + * @file token.h + *****************************************************************************/ +#ifndef _TOKEN_H +#define _TOKEN_H + +enum token_kind { + + TOK_EOF = 0, + + /* Single character punctionation. */ + TOK_XMARK = 33, + TOK_HASH = 35, + TOK_DOLLAR, + TOK_MOD, + TOK_AMPER, + TOK_LPAREN = 40, + TOK_RPAREN, + TOK_STAR, + TOK_PLUS, + TOK_COMMA, + TOK_MINUS, + TOK_DOT, + TOK_BSLASH, + TOK_COLON = 58, + TOK_SEMI, + TOK_LESS, + TOK_ASSIGN, + TOK_GREATER, + TOK_QMARK, + TOK_LBRACK = 91, + TOK_FSLASH, + TOK_RBRACK, + TOK_CARET, + TOK_UNDERSCORE, + TOK_BACKTICK, + TOK_LBRACE = 123, + TOK_PIPE, + TOK_RBRACE, + TOK_TILDE, + + /* Multi-character punctuation. */ + TOK_EQEQ = 128, + TOK_NOTEQ, + TOK_LTEQ, + TOK_GTEQ, + + TOK_INCR, + TOK_DECR, + + TOK_LOGAND, + TOK_LOGOR, + TOK_LSH, + TOK_RSH, + TOK_PLUSEQ, + TOK_MINUSEQ, + TOK_STAREQ, + TOK_SLASHEQ, + TOK_MODEQ, + TOK_LSHEQ, + TOK_RSHEQ, + TOK_ANDEQ, + TOK_XOREQ, + TOK_OREQ, + TOK_ELLIPSIS, + TOK_ARROW, + + /* Start of keywords. */ + TOK_ASM = 256, + TOK_AUTO, + TOK_BREAK, + TOK_CASE, + TOK_CHAR, + TOK_CONST, + TOK_CONTINUE, + TOK_DEFAULT, + TOK_DO, + TOK_DOUBLE, + TOK_ELSE, + TOK_ENUM, + TOK_EXTERN, + TOK_FLOAT, + TOK_FOR, + TOK_GOTO, + TOK_IF, + TOK_INLINE, + TOK_INT, + TOK_LONG, + TOK_OFFSETOF, + TOK_REGISTER, + TOK_RESTRICT, + TOK_RETURN, + TOK_SHORT, + TOK_SIGNED, + TOK_SIZEOF, + TOK_STATIC, + TOK_STRUCT, + TOK_SWITCH, + TOK_TYPEDEF, + TOK_TYPEOF, + TOK_UNION, + TOK_UNSIGNED, + TOK_VOID, + TOK_VOLATILE, + TOK_WHILE, + + TOK_PPSTR, + TOK_PPNUM, + TOK_LSTR, + + TOK_LCHAR, + TOK_CCHAR, + TOK_CINT, + TOK_CUINT, + TOK_CLLONG, + TOK_CULLONG, + TOK_CLONG, + TOK_CULONG, + + TOK_CFLOAT, + TOK_CDOUBLE, + TOK_CLDOUBLE, + + TOK_IDENT + +}; + +#include "int64.h" + +struct token { + + const char *start; + const char *caret; + + char *ident; + unsigned long len; + + enum token_kind kind; + + union { + + long double ld; + + double d; + float f; + + int64_s i; + + } val; + + int is_unget; + +}; + +extern struct token tok; +extern int tok_ident; + +void get_token (void); +void unget_token (struct token *saved_tok); + +struct punct { + + char *name; + enum token_kind kind; + +}; + +struct punct *find_punct (char *p); +int init_tokenizer (const char *ifile); + +#endif /* _TOKEN_H */ diff --git a/vector.c b/vector.c new file mode 100755 index 0000000..80e6377 --- /dev/null +++ b/vector.c @@ -0,0 +1,42 @@ +/****************************************************************************** + * @file vector.c + *****************************************************************************/ +#include +#include + +#include "vector.h" + +extern void *xrealloc (void *__ptr, unsigned long __size); + +void vec_adjust (struct vector *vec, long length) { + + if (vec->capacity <= length) { + + if (vec->capacity == 0) { + vec->capacity = 16; + } else { + vec->capacity <<= 1; + } + + vec->data = xrealloc (vec->data, sizeof (*(vec->data)) * vec->capacity); + + } + +} + +void *vec_pop (struct vector *vec) { + + if (!vec || !vec->length) { + return 0; + } + + return vec->data[--vec->length]; + +} + +void vec_push (struct vector *vec, void *elem) { + + vec_adjust (vec, vec->length); + vec->data[vec->length++] = elem; + +} diff --git a/vector.h b/vector.h new file mode 100755 index 0000000..7ba6848 --- /dev/null +++ b/vector.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * @file vector.h + *****************************************************************************/ +#ifndef _VECTOR_H +#define _VECTOR_H + +struct vector { + + void **data; + long capacity, length; + +}; + +void vec_adjust (struct vector *vec, long length); +void vec_push (struct vector *vec, void *elem); + +void *vec_pop (struct vector *vec); + +#endif /* _VECTOR_H */