From b9a30c8adc63baba259677263b8b011059f9ecf9 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Wed, 20 May 2026 06:56:31 +0100 Subject: [PATCH] Initial commit --- EXTENSIONS.md | 43 + LICENSE | 24 + Makefile.unix | 30 + Makefile.w32 | 21 + Makefile.wat | 31 + README.md | 124 + cc.c | 144 + cc.h | 46 + cstr.c | 69 + cstr.h | 20 + eval.c | 971 ++ eval.h | 9 + expr.c | 800 ++ expr.h | 13 + hashtab.c | 217 + hashtab.h | 36 + int64.c | 713 + int64.h | 71 + lex.c | 35 + lex.h | 18 + lib.c | 781 ++ lib.h | 51 + list.c | 39 + list.h | 16 + ll.c | 388 + ll.h | 13 + macro.c | 689 + macro.h | 32 + parse.c | 33959 ++++++++++++++++++++++++++++++++++++++++++++++++ parse.h | 18 + pp.c | 2031 +++ pp.h | 41 + report.c | 285 + report.h | 27 + stdint.h | 49 + sym.c | 7 + token.c | 2195 ++++ token.h | 171 + vector.c | 42 + vector.h | 19 + 40 files changed, 44288 insertions(+) create mode 100644 EXTENSIONS.md create mode 100755 LICENSE create mode 100755 Makefile.unix create mode 100755 Makefile.w32 create mode 100755 Makefile.wat create mode 100755 README.md create mode 100755 cc.c create mode 100755 cc.h create mode 100755 cstr.c create mode 100755 cstr.h create mode 100755 eval.c create mode 100755 eval.h create mode 100755 expr.c create mode 100755 expr.h create mode 100755 hashtab.c create mode 100755 hashtab.h create mode 100644 int64.c create mode 100644 int64.h create mode 100755 lex.c create mode 100755 lex.h create mode 100755 lib.c create mode 100755 lib.h create mode 100755 list.c create mode 100755 list.h create mode 100755 ll.c create mode 100755 ll.h create mode 100755 macro.c create mode 100755 macro.h create mode 100644 parse.c create mode 100644 parse.h create mode 100755 pp.c create mode 100755 pp.h create mode 100755 report.c create mode 100755 report.h create mode 100755 stdint.h create mode 100644 sym.c create mode 100755 token.c create mode 100755 token.h create mode 100755 vector.c create mode 100755 vector.h 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 */ -- 2.34.1