From: Robert Pengelly Date: Mon, 15 Jun 2026 08:46:51 +0000 (+0100) Subject: Experimental AMD64 support X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=94da49eed7164dd8f25903ad159b728845109b16;p=scc.git Experimental AMD64 support --- diff --git a/amd64.c b/amd64.c new file mode 100644 index 0000000..c23555f --- /dev/null +++ b/amd64.c @@ -0,0 +1,38330 @@ +/****************************************************************************** + * @file amd64.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 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); + +static int current_function_preserve_assignment64_regs = 0; +static int current_return_label = 0; + +/* + * Track backend-emitted temporary stack use modulo 16 so AMD64 call + * frame reservation can compensate for any outstanding PUSH/sub rsp,8 + * saves before a nested call. The normal function prologue leaves RSP + * 16-byte aligned in this backend, so calls need the current temporary + * depth plus the call reservation to be a multiple of 16. + */ +static int amd64_temp_stack_mod16 = 0; + +static void amd64_note_stack_sub (int bytes) { + + if (bytes > 0) { + amd64_temp_stack_mod16 = (amd64_temp_stack_mod16 + bytes) & 15; + } + +} + +static void amd64_note_stack_add (int bytes) { + + if (bytes > 0) { + amd64_temp_stack_mod16 = (amd64_temp_stack_mod16 - (bytes & 15)) & 15; + } + +} + +static void amd64_emit_push_reg_now (const char *reg); +static void amd64_emit_pop_reg_now (const char *reg); +static int amd64_align_call_stack_bytes (int bytes); + +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_return_pointer_depth = 0; +static int current_function_returns_aggregate = 0; +static int current_function_param_stack_bytes = 0; + +static enum token_kind current_function_calling_convention = TOK_EOF; +static int current_function_is_variadic = 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 assignment32_stop_before_condition_operator = 0; +static int assignment64_stop_before_condition_operator = 0; + +static int current_argument_starts_64bit_integer_now (void); +static int argument_text_starts_64bit_cast_now (const char *p); + +static void masm_flush_data_line (void); +static void emit_sub_rsp_now (int bytes); + +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 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 int find_member_info (const char *name, int *offset, int *size) { + return find_member_info_ex (name, offset, size, 0, 0, 0, 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_names[i].calling_convention = TOK_EOF; + + } + + typedef_name_count = 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, enum token_kind calling_convention, 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; + entry->calling_convention = TOK_EOF; + + } + + 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; + entry->calling_convention = calling_convention; + + 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 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; + +} + +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].array_dimensions = 0; + + global_symbols[i].is_dllimport = 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].calling_convention = TOK_EOF; + + { + + int pi; + + for (pi = 0; pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = 0; + global_symbols[i].param_unsigneds[pi] = 0; + global_symbols[i].param_floatings[pi] = 0; + global_symbols[i].param_pointer_depths[pi] = 0; + + } + + } + + global_symbols[i].import_call_stack_bytes = 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 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 int global_symbol_stdcall_stack_bytes (const char *name) { + + int i = find_global_symbol (name); + int pi; + int bytes = 0; + + if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || global_symbols[i].calling_convention != TOK_STDCALL || + + global_symbols[i].is_variadic) { + return 0; + + } + + for (pi = 0; pi < global_symbols[i].param_count && pi < 128; pi++) { + + int size = global_symbols[i].param_sizes[pi]; + + if (size < 1) { + size = DATA_PTR & 0x1f; + } + + bytes += ((size + (DATA_PTR & 0x1f) - 1) / (DATA_PTR & 0x1f)) * (DATA_PTR & 0x1f); + + } + + if (bytes <= 0 && global_symbols[i].import_call_stack_bytes > 0) { + bytes = global_symbols[i].import_call_stack_bytes; + } + + return bytes; + +} + +static void remember_global_symbol_import_call_stack_bytes (const char *name, int bytes) { + + int i = find_global_symbol (name); + + if (i < 0 || bytes <= 0) { + return; + } + + if (global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || !global_symbols[i].is_dllimport || global_symbols[i].calling_convention != TOK_STDCALL) { + return; + } + + if (global_symbol_stdcall_stack_bytes (name) <= 0) { + global_symbols[i].import_call_stack_bytes = bytes; + } + +} + +static const char *asm_global_symbol_name (const char *name) { + + static char buffers[8][512]; + static int index = 0; + + char *out, suffix[32]; + unsigned long avail, len; + + if (asm_symbol_is_internal (name)) { + return name; + } + + index = (index + 1) & 7; + + out = buffers[index]; + len = strlen (name); + + /* + * AMD64 does not use the i386 stdcall name suffix. __stdcall may be + * accepted as source syntax for compatibility, but the Microsoft x64 ABI + * uses one unified calling convention and symbols must not become + * name@bytes. + */ + suffix[0] = '\0'; + + avail = sizeof (buffers[0]) - 1 - strlen (suffix); + + if (!state->no_leading_underscore) { + + if (avail > 0) { + avail--; + } + + if (len > avail) { + len = avail; + } + + out[0] = '_'; + + memcpy (out + 1, name, len); + out[len + 1] = 0; + + } else { + + if (len > avail) { + len = avail; + } + + memcpy (out, name, len); + out[len] = 0; + + } + + if (suffix[0]) { + strcat (out, suffix); + } + + return out; + +} + +static const char *asm_global_import_symbol_name (const char *name) { + + static char buffers[8][512]; + static int index = 0; + + const char *decorated; + char *out; + + unsigned long len; + + if (!(decorated = asm_global_symbol_name (name)) || asm_symbol_is_internal (name)) { + return decorated; + } + + index = (index + 1) & 7; + + out = buffers[index]; + len = strlen (decorated); + + if (len > sizeof (buffers[0]) - 7) { + len = sizeof (buffers[0]) - 7; + } + + sprintf (out, "__imp_"); + + memcpy (out + 6, decorated, len); + out[len + 6] = 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_dllimport (const char *name, int is_dllimport) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + global_symbols[i].is_dllimport = is_dllimport ? 1 : 0; + + if (is_dllimport) { + global_symbols[i].is_extern = 1; + } + + } + +} + +static int get_global_symbol_dllimport (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_dllimport ? 1 : 0; + } + + 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 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_dimensions (const char *name, int dims) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].array_dimensions = dims > 0 ? dims : 0; + } + +} + +static int get_global_symbol_array_dimensions (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].array_dimensions; + } + + 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 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 int get_global_function_returns_floating (const char *name) { + + int i = find_global_symbol (name); + + if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION) { + return 0; + } + + if (global_symbols[i].is_floating) { + return 1; + } + + /* + * Do not infer floating return types from the masked object size. + * + * DATA_INT and DATA_FLOAT both have a 4-byte payload size, so checking + * only (size & 0x1f) makes every int-returning function look like it + * returns float. That breaks expressions such as: + * + * printf("%p", main); + * + * because the function designator gets loaded as a floating object from + * memory instead of as a plain code address. The declaration/definition + * paths already record real floating function returns in is_floating. + */ + return global_symbols[i].is_floating ? 1 : 0; + +} + +static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) { + + int i = find_global_symbol (name); + + if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (i >= 0) { + + global_symbols[i].pointer_depth = pointer_depth; + global_symbols[i].pointed_size = pointed_size; + + global_symbols[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + global_symbols[i].pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; + + } + +} + +static int get_global_symbol_pointed_is_floating (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].pointed_is_floating; + } + + return 0; + +} + +static int get_global_symbol_pointed_is_unsigned (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].pointed_is_unsigned; + } + + return 0; + +} + +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 void set_global_symbol_calling_convention (const char *name, enum token_kind calling_convention) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].calling_convention = calling_convention; + } + +} + +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 int get_global_symbol_param_size (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128 && global_symbols[i].param_sizes[param_index] > 0) { + return global_symbols[i].param_sizes[param_index]; + } + + return DATA_PTR & 0x1f; + +} + +static int get_global_symbol_param_unsigned (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128) { + return global_symbols[i].param_unsigneds[param_index] ? 1 : 0; + } + + return 0; + +} + +static int get_global_symbol_param_floating (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128) { + return global_symbols[i].param_floatings[param_index] ? 1 : 0; + } + + 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 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_dllimport = 0; + 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].pointer_depth = 0; + global_symbols[global_symbol_count].pointed_size = 0; + global_symbols[global_symbol_count].pointed_is_floating = 0; + global_symbols[global_symbol_count].pointed_is_unsigned = 0; + global_symbols[global_symbol_count].returns_void = 0; + global_symbols[global_symbol_count].calling_convention = TOK_EOF; + + { + + int pi; + + for (pi = 0; pi < 128; pi++) { + + global_symbols[global_symbol_count].param_sizes[pi] = 0; + global_symbols[global_symbol_count].param_unsigneds[pi] = 0; + global_symbols[global_symbol_count].param_floatings[pi] = 0; + global_symbols[global_symbol_count].param_pointer_depths[pi] = 0; + + } + + } + + global_symbols[global_symbol_count].import_call_stack_bytes = 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].calling_convention = TOK_EOF; + 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 rbp, rsp\n"); + + if (start) { + start += strlen (" mov rbp, rsp\n"); + } else { + + start = strstr (asm_text, " movq %rsp, %rbp\n"); + + if (start) { + start += strlen (" movq %rsp, %rbp\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); + +static long function_frame_saved_assignment64_bytes (void) { + + /* + * Deferred frame restoration only knows whether assignment64 register + * preservation was requested, not which operator triggered it. + * + * emit_preserve_assignment64_regs() saves: + * - RBX only for most operators (8 bytes) + * - RBX+RSI+RDI for multiply/divide/modulo paths (24 bytes) + * + * Returning a hard-coded 24 here over-allocates the frame and causes the + * deferred epilogue to restore registers that were never saved. + * + * Until the exact save set is tracked, defer only the always-saved RBX. + */ + return current_function_preserve_assignment64_regs == 2 ? 24 : (current_function_preserve_assignment64_regs ? 8 : 0); + +} + +static void emit_function_frame_adjust_text (char **dst, long frame_size) { + + char buf[128]; + + long save_bytes; + long total_frame_bytes; + + int n; + + if (!dst) { + return; + } + + save_bytes = function_frame_saved_assignment64_bytes (); + + /* + * AMD64 calls must see RSP 16-byte aligned at the call instruction. + * After the usual "push rbp" prologue, RSP is aligned. Any local + * frame allocation plus any callee-save pushes emitted here must therefore + * consume a multiple of 16 bytes in total. + * + * The old code rounded only the local frame to 4 bytes and ignored the + * optional rbx/rsi/rdi saves. With those three pushes, a function with no + * locals consumed 24 bytes and every later call was misaligned. + */ + if (frame_size < 0) { + frame_size = 0; + } + + total_frame_bytes = frame_size + save_bytes; + total_frame_bytes = (total_frame_bytes + 15) & ~15L; + + frame_size = total_frame_bytes - save_bytes; + + if (frame_size <= 0 && save_bytes <= 0) { + return; + } + + if (frame_size > 2147483647L) { + frame_size = 2147483647L; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (save_bytes) { + + append_inline_text (dst, " push rbx\n", strlen (" push rbx\n")); + + if (save_bytes == 24) { + + append_inline_text (dst, " push rsi\n", strlen (" push rsi\n")); + append_inline_text (dst, " push rdi\n", strlen (" push rdi\n")); + + } + + } + + if (frame_size > 0) { + + n = sprintf (buf, " sub rsp, %ld\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + } else { + + if (save_bytes) { + + append_inline_text (dst, " pushq %rbx\n", strlen (" pushq %rbx\n")); + + if (save_bytes == 24) { + + append_inline_text (dst, " pushq %rsi\n", strlen (" pushq %rsi\n")); + append_inline_text (dst, " pushq %rdi\n", strlen (" pushq %rdi\n")); + + } + + } + + if (frame_size > 0) { + + n = sprintf (buf, " subq $%ld, %%rsp\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + } + +} + +static void emit_function_frame_restore_text (char **dst, long frame_size) { + + char buf[128]; + + long save_bytes; + long total_frame_bytes; + + int n; + + if (!dst) { + return; + } + + save_bytes = function_frame_saved_assignment64_bytes (); + + if (frame_size < 0) { + frame_size = 0; + } + + total_frame_bytes = frame_size + save_bytes; + total_frame_bytes = (total_frame_bytes + 15) & ~15L; + + frame_size = total_frame_bytes - save_bytes; + + if (frame_size > 2147483647L) { + frame_size = 2147483647L; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (frame_size > 0) { + + n = sprintf (buf, " add rsp, %ld\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + if (save_bytes) { + + if (save_bytes == 24) { + + append_inline_text (dst, " pop rdi\n", strlen (" pop rdi\n")); + append_inline_text (dst, " pop rsi\n", strlen (" pop rsi\n")); + + } + + append_inline_text (dst, " pop rbx\n", strlen (" pop rbx\n")); + + } + + } else { + + if (frame_size > 0) { + + n = sprintf (buf, " addq $%ld, %%rsp\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + if (save_bytes) { + + if (save_bytes == 24) { + + append_inline_text (dst, " popq %rdi\n", strlen (" popq %rdi\n")); + append_inline_text (dst, " popq %rsi\n", strlen (" popq %rsi\n")); + + } + + append_inline_text (dst, " popq %rbx\n", strlen (" popq %rbx\n")); + + } + + } + +} + +static char *replace_function_frame_placeholder (const char *text, long frame_size) { + + const char *frame_marker = "__SCC_FRAME_PLACEHOLDER__\n"; + const char *restore_marker = "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"; + + const char *pr, *pf, *p; + const char *marker, *last; + + char *out = 0; + size_t marker_len; + + if (!text) { + return 0; + } + + last = text; + + for (;;) { + + pf = strstr (last, frame_marker); + pr = strstr (last, restore_marker); + + if (!pf && !pr) { + break; + } + + if (pf && (!pr || pf < pr)) { + p = pf; + marker = frame_marker; + } else { + p = pr; + marker = restore_marker; + } + + marker_len = strlen (marker); + append_inline_text (&out, last, (size_t) (p - last)); + + if (marker == frame_marker) { + emit_function_frame_adjust_text (&out, frame_size); + } else { + emit_function_frame_restore_text (&out, frame_size); + } + + last = p + marker_len; + + } + + append_inline_text (&out, last, strlen (last)); + return out; + +} + +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, "rbp", 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) % 8) != 0) { + return 0; + } + + param_index = (offset - 8) / 8; + + 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 [rbp + 8] maps to [rsp], + * [rbp + 16] maps to [rsp + 8], [rbp + 24] maps to [rsp + 16], etc. + * If the inlined body changes RSP temporarily, stack_bytes keeps all + * parameter references pointed at the same argument copies. + */ + stack_offset = stack_bytes + (param_index * 8); + + if (stack_offset == 0) { + fprintf (state->ofp, "[rsp]"); + } else if (stack_offset > 0) { + fprintf (state->ofp, "[rsp + %d]", stack_offset); + } else { + fprintf (state->ofp, "[rsp - %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 8; + } + + if (len >= 3 && strncmp (p, "pop", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) { + return -8; + } + + if (len >= 8 && (strncmp (p, "sub rsp,", 8) == 0 || strncmp (p, "add rsp,", 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, "subq $", 6) == 0 || strncmp (p, "addq $", 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, ", %rsp", 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_rax_imm (const char *line, int64_s *value) { + + const char *p = line; + + char buf[128]; + size_t len = 0; + + if (!value) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov rax,", 8) != 0) { + return 0; + } + + p += 8; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p == '-') { + + if (len + 1 >= sizeof (buf)) { + return 0; + } + + buf[len++] = *p++; + + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while (*p >= '0' && *p <= '9') { + + if (len + 1 >= sizeof (buf)) { + return 0; + } + + buf[len++] = *p++; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '\0') { + return 0; + } + + buf[len] = '\0'; + parse_string_to_i64 (value, buf); + + return 1; + +} + +static int inline_parse_mov_rdx_imm (const char *line, int64_s *value) { + + const char *p = line; + + char buf[128]; + size_t len = 0; + + if (!value) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov rdx,", 8) != 0) { + return 0; + } + + p += 8; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (*p == '-') { + + if (len + 1 >= sizeof (buf)) { + return 0; + } + + buf[len++] = *p++; + + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while (*p >= '0' && *p <= '9') { + + if (len + 1 >= sizeof (buf)) { + return 0; + } + + buf[len++] = *p++; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '\0') { + return 0; + } + + buf[len] = '\0'; + parse_string_to_i64 (value, buf); + + return 1; + +} + +static void inline_format_mov_imm64 (char *buf, size_t buf_size, const char *reg, int64_s value) { + + if (!buf || buf_size == 0 || !reg) { + return; + } + + sprintf (buf, " mov %s, 0%08lX%08lXh\n", reg, value.high & U32_MASK, value.low & U32_MASK); + +} + +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 void inline_i64_from_long (int64_s *out, long v) { + sext64 (out, v); +} + +static void inline_sar64 (int64_s *a, int n) { + + int fill; + int i; + + if (!a || n <= 0) { + return; + } + + if (n > 63) { + n = 63; + } + + fill = int64_is_negative (*a) ? 1 : 0; + + for (i = 0; i < n; i++) { + + a->low = ((a->low >> 1) | ((a->high & 1UL) << 31)) & U32_MASK; + a->high = ((a->high >> 1) | (fill ? 0x80000000UL : 0)) & U32_MASK; + + } + +} + +static void inline_abs64 (int64_s *v) { + + if (v && int64_is_negative (*v)) { + neg64 (v); + } + +} + +static int inline_fold_binary_rax_rdx (const char *line, int64_s lhs, int64_s rhs, int64_s *out) { + + int64_s r = lhs; + + if (!line || !out) { + return 0; + } + + if (inline_line_is_exact (line, "add rax, rdx")) { + add64 (&r, rhs); + } else if (inline_line_is_exact (line, "sub rax, rdx")) { + sub64 (&r, rhs); + } else if (inline_line_is_exact (line, "imul rax, rdx")) { + mul64 (&r, rhs); + } else if (inline_line_is_exact (line, "and rax, rdx")) { + and64 (&r, rhs); + } else if (inline_line_is_exact (line, "or rax, rdx")) { + or64 (&r, rhs); + } else if (inline_line_is_exact (line, "xor rax, rdx")) { + xor64 (&r, rhs); + } else { + return 0; + } + + *out = r; + return 1; + +} + +static int inline_fold_shift_rax_rdx (const char *line, int64_s lhs, int64_s rhs, int64_s *out) { + + int64_s r = lhs; + unsigned int count; + + if (!line || !out) { + return 0; + } + + count = (unsigned int) (rhs.low & 63UL); + + if (inline_line_is_exact (line, "shl rax, cl")) { + shl64 (&r, count); + } else if (inline_line_is_exact (line, "sal rax, cl")) { + shl64 (&r, count); + } else if (inline_line_is_exact (line, "sar rax, cl")) { + inline_sar64 (&r, count); + } else if (inline_line_is_exact (line, "shr rax, cl")) { + shr64 (&r, count); + } else { + return 0; + } + + *out = r; + return 1; + +} + +static int inline_fold_div_rax_rdx (int64_s lhs, int64_s rhs, int64_s *out) { + + int64_s a = lhs; + int64_s b = rhs; + + int neg; + + if (!out || is_zero64 (b)) { + return 0; + } + + neg = (int64_is_negative (a) ? 1 : 0) ^ (int64_is_negative (b) ? 1 : 0); + + inline_abs64 (&a); + inline_abs64 (&b); + + div64 (&a, b); + + if (neg) { + neg64 (&a); + } + + *out = a; + return 1; + +} + +static int inline_fold_mod_rax_rdx (int64_s lhs, int64_s rhs, int64_s *out) { + + int64_s a = lhs; + int64_s b = rhs; + + int neg; + + if (!out || is_zero64 (b)) { + return 0; + } + + neg = int64_is_negative (a) ? 1 : 0; + + inline_abs64 (&a); + inline_abs64 (&b); + + mod64 (&a, b); + + if (neg) { + neg64 (&a); + } + + *out = a; + 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, int64_s value) { + + if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) { + return is_zero64 (value); + } + + if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) { + return !is_zero64 (value); + } + + if (strcmp (op, "js") == 0) { + return int64_is_negative (value); + } + + if (strcmp (op, "jns") == 0) { + return !int64_is_negative (value); + } + + return 0; + +} + +static int inline_eval_cmp_jump (const char *op, int64_s lhs, int64_s rhs) { + + if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) { + return eq64 (lhs, rhs); + } + + if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) { + return neq64 (lhs, rhs); + } + + if (strcmp (op, "jg") == 0) { + return gt64_signed (lhs, rhs); + } + + if (strcmp (op, "jge") == 0) { + return gte64_signed (lhs, rhs); + } + + if (strcmp (op, "jl") == 0) { + return lt64_signed (lhs, rhs); + } + + if (strcmp (op, "jle") == 0) { + return lte64_signed (lhs, rhs); + } + + if (strcmp (op, "ja") == 0) { + return gt64_unsigned (lhs, rhs); + } + + if (strcmp (op, "jae") == 0) { + return gte64_unsigned (lhs, rhs); + } + + if (strcmp (op, "jb") == 0) { + return lt64_unsigned (lhs, rhs); + } + + if (strcmp (op, "jbe") == 0) { + return lte64_unsigned (lhs, rhs); + } + + 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_rsp_reference (const char *p, int *offset) { + + const char *start = p; + + int off = 0; + int neg = 0; + + if (strncmp (p, "[rsp]", 5) == 0) { + + *offset = 0; + return 5; + + } + + if (strncmp (p, "[rsp + ", 7) == 0) { + p += 7; + } else if (strncmp (p, "[rsp+", 5) == 0) { + p += 5; + } else if (strncmp (p, "[rsp - ", 7) == 0) { + neg = 1; + p += 7; + } else if (strncmp (p, "[rsp-", 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_rax_to_rsp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov qword ptr ", 14) == 0) { + p += 14; + } else if (strncmp (p, "mov qword ", 10) == 0) { + p += 10; + } else { + return 0; + } + + n = inline_parse_rsp_reference (p, offset); + + if (!n) { + return 0; + } + + p += n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, ", rax", 5) != 0) { + return 0; + } + + p += 5; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '\0'; + +} + +static int inline_parse_load_rax_from_rsp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov rax, qword ptr ", 19) == 0) { + p += 19; + } else if (strncmp (p, "mov rax, qword ", 15) == 0) { + p += 15; + } else { + return 0; + } + + n = inline_parse_rsp_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_rdx_from_rsp (const char *line, int *offset) { + + const char *p = line; + int n; + + while (*p == ' ' || *p == '\t') { + p++; + } + + if (strncmp (p, "mov rdx, qword ptr ", 19) == 0) { + p += 19; + } else if (strncmp (p, "mov rdx, qword ", 15) == 0) { + p += 15; + } else { + return 0; + } + + n = inline_parse_rsp_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_rsp_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 qword ptr ", 14) == 0) { + p += 14; + } else if (strncmp (p, "add qword ", 10) == 0) { + p += 10; + } else if (strncmp (p, "sub qword ptr ", 14) == 0) { + + is_sub = 1; + p += 14; + + } else if (strncmp (p, "sub qword ", 10) == 0) { + + is_sub = 1; + p += 10; + + } else { + return 0; + } + + n = inline_parse_rsp_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, int64_s *slot_value) { + + int i; + + for (i = 0; i < 64; i++) { + + slot_valid[i] = 0; + slot_value[i].low = 0; + slot_value[i].high = 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; + + int64_s slot_value[64]; + int64_s rax_value; + + int slot_valid[64]; + int slot_real_read[64]; + + int rax_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; + } + + rax_value.low = 0; + rax_value.high = 0; + + 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; + int64_s 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; + + rax_valid = 0; + continue; + + } + + if (delta < 0 && alloc_line >= 0 && -delta == alloc_bytes) { + + free_line = i; + rax_valid = 0; + + continue; + + } + + if (inline_parse_mov_rax_imm (buf, &value)) { + + rax_value = value; + rax_valid = 1; + + continue; + + } + + if (inline_parse_store_rax_to_rsp (buf, &offset)) { + + if (offset >= 0 && (offset % 8) == 0) { + + slot = offset / 8; + + if (slot >= 0 && slot < 64) { + + store_slot[i] = slot; + + if (rax_valid) { + + slot_valid[slot] = 1; + slot_value[slot] = rax_value; + + } else { + slot_valid[slot] = 0; + } + + } + + } + + continue; + + } + + if (inline_parse_load_rax_from_rsp (buf, &offset)) { + + if (offset >= 0 && (offset % 8) == 0) { + + slot = offset / 8; + + if (slot >= 0 && slot < 64 && slot_valid[slot]) { + + char tmp[128]; + inline_format_mov_imm64 (tmp, sizeof (tmp), "rax", slot_value[slot]); + + repl[i] = inline_dup_text_slice (tmp, strlen (tmp)); + + rax_value = slot_value[slot]; + rax_valid = 1; + + continue; + + } + + if (slot >= 0 && slot < 64) { + slot_real_read[slot] = 1; + } + + } + + rax_valid = 0; + continue; + } + + if (inline_parse_load_rdx_from_rsp (buf, &offset)) { + + if (offset >= 0 && (offset % 8) == 0) { + + slot = offset / 8; + + if (slot >= 0 && slot < 64 && slot_valid[slot]) { + + char tmp[128]; + inline_format_mov_imm64 (tmp, sizeof (tmp), "rdx", 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_rsp_imm1 (buf, &offset, &mem_delta)) { + + if (offset >= 0 && (offset % 8) == 0) { + + slot = offset / 8; + + if (slot >= 0 && slot < 64) { + + if (slot_valid[slot]) { + + int64_s delta_value; + + inline_i64_from_long (&delta_value, mem_delta); + add64 (&slot_value[slot], delta_value); + + skip[i] = 1; + + } + + continue; + } + } + + rax_valid = 0; + continue; + + } + + if (strstr (buf, "rsp")) { + + inline_clear_slots (slot_valid, slot_value); + rax_valid = 0; + + } else if (strstr (buf, "rax")) { + rax_valid = 0; + } + + } + + if (alloc_line >= 0 && free_line > alloc_line && alloc_bytes > 0) { + + int any_remaining_rsp = 0; + int slots = alloc_bytes / 8; + + 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], "rsp")) { + any_remaining_rsp = 1; + break; + } + + } else if (lines[i] && strstr (lines[i], "rsp")) { + + any_remaining_rsp = 1; + break; + + } + + } + + if (!any_remaining_rsp) { + + 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 precrding + * constant load was only there to feed that copy. Drop it too; + * otherwise multi-argument inline calls leave noise like: + * + * mov rax, 1 + * mov rax, 2 + * + * after both temporary argument stores have been removed. + */ + for (i = 0; i < count; i++) { + + int64_s 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_rax_imm (lines[j], &ignored_value)) { + skip[j] = 1; + } + + break; + + } + + } + + } + + } + + (void) can_remove_area; + + for (i = 0; i < count; i++) { + + int64_s first_value; + int j; + + if (skip[i] || !lines[i] || !inline_parse_mov_rax_imm (lines[i], &first_value)) { + continue; + } + + for (j = i + 1; j < count; j++) { + + int64_s next_value; + const char *check; + + if (skip[j]) { + continue; + } + + check = repl[j] ? repl[j] : lines[j]; + + if (!check) { + continue; + } + + if (!strstr (check, "rax")) { + continue; + } + + if (inline_parse_mov_rax_imm (check, &next_value) && eq64 (next_value, first_value)) { + skip[i] = 1; + } + + break; + + } + + } + + for (i = 0; i + 3 < count; i++) { + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_rax_rdx (l2, lhs, rhs, &lhs)) { + continue; + } + + if (!inline_line_is_exact (l3, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_rax_rdx (l2, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l3, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l4, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_rax_rdx (l2, lhs, rhs, &lhs)) { + continue; + } + + if (!inline_line_is_exact (l3, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_rax_rdx (l2, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l3, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l4, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx")) { + continue; + } + + if (!inline_fold_shift_rax_rdx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l4, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx")) { + continue; + } + + if (!inline_fold_shift_rax_rdx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l4, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l5, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx")) { + continue; + } + + if (!inline_fold_shift_rax_rdx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l4, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx")) { + continue; + } + + if (!inline_fold_shift_rax_rdx (l3, lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l4, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l5, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx")) { + continue; + } + + if (!inline_fold_div_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l5, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx")) { + continue; + } + + if (!inline_fold_div_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l5, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l6, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx") || !inline_line_is_exact (l5, "mov rax, rdx")) { + continue; + } + + if (!inline_fold_mod_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_line_is_exact (l6, "test rax, rax")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s cmp_rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx") || !inline_line_is_exact (l5, "mov rax, rdx")) { + continue; + } + + if (!inline_fold_mod_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l6, &cmp_rhs)) { + continue; + } + + if (!inline_line_is_exact (l7, "cmp rax, rdx")) { + 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_fold_binary_rax_rdx (l2, lhs, rhs, &folded)) { + continue; + } + + inline_format_mov_imm64 (tmp, sizeof (tmp), "rax", 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx")) { + continue; + } + + if (!inline_fold_shift_rax_rdx (l3, lhs, rhs, &folded)) { + continue; + } + + inline_format_mov_imm64 (tmp, sizeof (tmp), "rax", 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx")) { + continue; + } + + if (!inline_fold_div_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + inline_format_mov_imm64 (tmp, sizeof (tmp), "rax", 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++) { + + int64_s lhs; + int64_s rhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "mov rcx, rdx") || !inline_line_is_exact (l3, "cdq") || + !inline_line_is_exact (l4, "idiv rcx") || !inline_line_is_exact (l5, "mov rax, rdx")) { + continue; + } + + if (!inline_fold_mod_rax_rdx (lhs, rhs, &folded)) { + continue; + } + + inline_format_mov_imm64 (tmp, sizeof (tmp), "rax", 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 rax, 9 + * mov rdx, 9 + * cmp rax, rdx + * jne Lx + * + * are folded after the arithmetic result becomes visible. + */ + for (i = 0; i + 3 < count; i++) { + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp rax, rdx")) { + 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 rax, 9 + * ; skipped old rhs/op lines + * mov rdx, 9 + * cmp rax, rdx + * 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; + + int64_s lhs; + int64_s 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_rax_imm (l0, &lhs)) { + continue; + } + + if (!inline_parse_mov_rdx_imm (l1, &rhs)) { + continue; + } + + if (!inline_line_is_exact (l2, "cmp rax, rdx")) { + 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); + + } + + } + + scc_close (*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, "rax") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%s\n", reg); + } + + } + + return 1; + +} + +static int local_array_pointer_step_size (const struct local_symbol *sym); +static int global_array_pointer_step_size (const char *name); + +static int aggregate_tag_size_or_zero (const char *tag_name) { + + struct aggregate_tag_entry *entry; + + if (!tag_name || !tag_name[0]) { + return 0; + } + + entry = find_aggregate_tag (tag_name, 0); + + if (entry && entry->size > 0) { + return entry->size; + } + + return 0; + +} + +static long current_local_stack_size = 0; +static long current_block_cleanup_bytes = 0; + +static long current_function_frame_size = 0; + +static int current_function_frame_label = -1; +static int current_function_frame_deferred = 0; +static int current_function_frame_enabled = 0; +static int current_function_uses_single_frame = 0; + +static int next_function_frame_label = 1; + +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; + current_block_cleanup_bytes = 0; + current_function_frame_size = 0; + current_function_uses_single_frame = 0; + current_function_preserve_assignment64_regs = 0; + current_function_is_variadic = 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(%rbp), 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, 8); + + 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].array_dimensions = 0; + local_symbols[local_symbol_count].pointer_depth = 0; + local_symbols[local_symbol_count].pointed_size = 0; + local_symbols[local_symbol_count].pointed_is_floating = 0; + local_symbols[local_symbol_count].pointed_is_unsigned = 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; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_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_symbols[local_symbol_count].array_dimensions = 0; + local_symbols[local_symbol_count].pointer_depth = 0; + local_symbols[local_symbol_count].pointed_size = 0; + local_symbols[local_symbol_count].pointed_is_floating = 0; + local_symbols[local_symbol_count].pointed_is_unsigned = 0; + local_symbols[local_symbol_count].pointed_tag_name = 0; + local_symbols[local_symbol_count].tag_name = 0; + + local_symbol_count++; + +} + +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_array_dimensions (const char *name, int dims) { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym) { + sym->array_dimensions = dims > 0 ? dims : 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; + + } + + } + +} + +static int local_symbol_exists_in_current_scope (const char *name, int start_count); + +static int find_pending_param (const char *name) { + return find_pending_param_from (name, 0); +} + +static void update_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size, int pointed_is_unsigned) { + + int i = find_pending_param (name); + + if (i < 0) { + return; + } + + if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + if (align < 1) { + align = type_alignment (size); + } + + pending_params[i].size = size; + pending_params[i].align = align; + pending_params[i].is_unsigned = is_unsigned ? 1 : 0; + pending_params[i].is_floating = is_floating ? 1 : 0; + pending_params[i].pointer_depth = pointer_depth; + pending_params[i].pointed_size = pointed_size > 0 ? pointed_size : 0; + pending_params[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + pending_params[i].pointed_is_unsigned = pointer_depth > 0 ? (pointed_is_unsigned ? 1 : 0) : 0; + + if (pending_params[i].pointed_tag_name) { + + free (pending_params[i].pointed_tag_name); + pending_params[i].pointed_tag_name = 0; + + } + + if (pointer_depth > 0 && parsed_type_tag_name[0]) { + pending_params[i].pointed_tag_name = xstrdup (parsed_type_tag_name); + } + +} + +static int mark_pending_param_knr_declared (const char *name, const char *name_start, const char *name_caret, unsigned long name_line) { + + int i = find_pending_param (name); + + if (i < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "K&R parameter '%s' is not in function parameter list", name); + return 0; + + } + + if (pending_params[i].knr_declared) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "duplicate K&R parameter declaration for '%s'", name); + return 0; + + } + + pending_params[i].knr_declared = 1; + return 1; + +} + +static void remove_duplicate_pending_params (void) { + + int i; + + for (i = 0; i < pending_param_count; i++) { + + int j; + + if (!pending_params[i].name) { + continue; + } + + j = i + 1; + + while (j < pending_param_count) { + + if (pending_params[j].name && strcmp (pending_params[i].name, pending_params[j].name) == 0) { + + int k; + + if (pending_params[j].name) { + free (pending_params[j].name); + } + + if (pending_params[j].pointed_tag_name) { + free (pending_params[j].pointed_tag_name); + } + + for (k = j; k + 1 < pending_param_count; k++) { + pending_params[k] = pending_params[k + 1]; + } + + pending_param_count--; + + pending_params[pending_param_count].name = 0; + pending_params[pending_param_count].size = 0; + pending_params[pending_param_count].align = 0; + pending_params[pending_param_count].is_unsigned = 0; + pending_params[pending_param_count].is_floating = 0; + pending_params[pending_param_count].pointer_depth = 0; + pending_params[pending_param_count].pointed_size = 0; + pending_params[pending_param_count].pointed_tag_name = 0; + + continue; + + } + + j++; + + } + + } + +} + +static void copy_pending_params_to_global_symbol (const char *name) { + + int i = find_global_symbol (name); + int pi; + + if (i < 0) { + return; + } + + for (pi = 0; pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = 0; + global_symbols[i].param_unsigneds[pi] = 0; + global_symbols[i].param_floatings[pi] = 0; + global_symbols[i].param_pointer_depths[pi] = 0; + + } + + for (pi = 0; pi < pending_param_count && pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = pending_params[pi].size; + global_symbols[i].param_unsigneds[pi] = pending_params[pi].is_unsigned; + global_symbols[i].param_floatings[pi] = pending_params[pi].is_floating; + global_symbols[i].param_pointer_depths[pi] = pending_params[pi].pointer_depth; + + } + +} + +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 (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (sym) { + + sym->pointer_depth = pointer_depth; + sym->pointed_size = pointed_size; + + sym->pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + sym->pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; + + if (sym->pointed_tag_name) { + + free (sym->pointed_tag_name); + sym->pointed_tag_name = 0; + + } + + if ((pointer_depth > 0 || sym->is_array) && 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 ? 24 : 16; + 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_is_floating = pending_params[i].pointed_is_floating; + local_symbols[local_symbol_count].pointed_is_unsigned = pending_params[i].pointed_is_unsigned; + local_symbols[local_symbol_count].is_array = 0; + local_symbols[local_symbol_count].array_element_size = 0; + local_symbols[local_symbol_count].array_dimensions = 0; + 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 (state->syntax & ASM_SYNTAX_MASM) { + masm_flush_data_line (); + } + + 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 current_token_spells_identifier_now (const char *name) { + + const char *p; + size_t len; + + if (!name || !*name) { + return 0; + } + + if (tok.ident && strcmp (tok.ident, name) == 0) { + return 1; + } + + /* + * Some declaration modifiers are real tokens rather than TOK_IDENT. + * Use the original source spelling as a generic fallback so the + * declaration parser can accept them in either specifier position: + * + * __dllexport void f(void); + * void __dllexport f(void); + */ + p = tok.caret ? tok.caret : tok.start; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t') { + p++; + } + + len = strlen (name); + + if (strncmp (p, name, len) != 0) { + return 0; + } + + if ((p[len] >= 'A' && p[len] <= 'Z') || + (p[len] >= 'a' && p[len] <= 'z') || + (p[len] >= '0' && p[len] <= '9') || + p[len] == '_') { + return 0; + } + + return 1; + +} + +static void consume_post_type_declaration_modifiers_now (int *out_dllimport, int *out_dllexport) { + + for (;;) { + + if (current_token_spells_identifier_now ("__dllimport")) { + + if (out_dllimport) { + *out_dllimport = 1; + } + + parsed_dllimport = 1; + get_token (); + + continue; + + } + + if (current_token_spells_identifier_now ("__dllexport")) { + + if (out_dllexport) { + *out_dllexport = 1; + } + + parsed_dllexport = 1; + get_token (); + + continue; + + } + + break; + + } + +} + +static int last_deref_cast_type_is_floating = 0; + +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_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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_array_count; + + char *name = 0; + + int size = DATA_INT & 0x1f; + int ok = 0, i; + int cast_is_floating = 0; + + last_deref_cast_type_is_floating = 0; + + 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; + cast_is_floating = parsed_type_is_floating; + + if (tok.kind != TOK_RPAREN) { + + parse_declarator (&name); + + /* + * This parser is called after a leading unary '*', for forms such + * as *(DATA_LLONG *)p. The '*' outside the cast performs the + * dereference, so the size needed by the caller is the pointed-at + * object size, not DATA_PTR. However, forms produced by va_arg() + * for pointer types are one level deeper, e.g. char * becomes + * *(char **). In that case the dereferenced object is itself a + * pointer and must be loaded with pointer width. + */ + if (declarator_is_pointer && declarator_pointer_depth > 1) { + size = DATA_PTR; + } + + } + + if (tok.kind == TOK_RPAREN) { + + get_token (); + ok = 1; + + last_deref_cast_type_is_floating = cast_is_floating; + + } 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_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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + if (out_size) { + *out_size = size; + } + + return ok; + +} + +static void emit_load_assignment_rhs_expression_to_reg (const char *reg); +static void emit_load_floating_rhs_expression_now (int result_size); +static void emit_store_floating_to_local_now (long offset, int size); + +static void consume_comma_expression_tail_before_semi (void) { + + while (tok.kind == TOK_COMMA) { + + get_token (); + emit_load_assignment_rhs_expression_to_reg ("rax"); + + } + +} + +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 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 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_first_array_count = declarator_array_count; + declarator_last_array_count = declarator_array_count; + declarator_array_dimensions = 1; + + 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)) { + + int param_base_size; + + parse_type_spec (); + param_base_size = parsed_type_size; + + for (;;) { + + char *name = 0; + + int param_size; + int param_pointer_depth; + + preserve_pending_params++; + parse_declarator (&name); + preserve_pending_params--; + + if (name) { + + param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size); + param_pointer_depth = declarator_pointer_depth; + + if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { + param_pointer_depth = 1; + } + + if (mark_pending_param_knr_declared (name, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line)) { + + update_pending_param (name, param_size, type_alignment (param_size), + (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), + param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); + + } + + 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_deferred_function_frame_placeholder (void); +static void patch_deferred_function_frame (void); + +static const char *amd64_integer_arg_reg (int index); +static const char *amd64_xmm_arg_reg (int index); +static void amd64_emit_load_xmm0_to_floating_stack_now (int size); + +static void emit_home_incoming_parameter_regs (void) { + + int hidden = current_function_returns_aggregate ? 1 : 0; + int i; + + if (!state->ofp) { + return; + } + + /* + * Microsoft x64 passes the first four argument slots in registers. The + * caller reserves home space, but it does not populate it. This backend + * represents parameters as positive [rbp + N] locations, so home the + * incoming registers at function entry before normal parameter loads can + * read them. + * + * After push rbp / mov rbp,rsp: + * [rbp + 8] = return address + * [rbp + 16] = argument slot 0 home area + * [rbp + 24] = argument slot 1 home area + * [rbp + 32] = argument slot 2 home area + * [rbp + 40] = argument slot 3 home area + */ + if (current_function_returns_aggregate) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov qword [rbp + 16], rcx\n" : " mov qword ptr [rbp + 16], rcx\n"); + } else { + fprintf (state->ofp, " movq %%rcx, 16(%%rbp)\n"); + } + + } + + /* + * pending_params has already been converted into local_symbols by the + * time emit_function_start() calls us. The old loop used + * pending_param_count here, which is zero after clear_pending_params(), + * so no incoming RCX/RDX/R8/R9 values were ever copied to the positive + * [rbp+N] parameter slots. The generated function bodies then read + * stale/zero home slots such as [rbp+16], causing crashes in routines + * like strchr(). + * + * At this point local_symbols contains only the installed parameters; + * block locals are parsed later. + */ + for (i = 0; i < local_symbol_count; i++) { + + int arg_index = i + hidden; + int offset = local_symbols[i].offset; + + const char *ireg = amd64_integer_arg_reg (arg_index); + const char *xreg = amd64_xmm_arg_reg (arg_index); + + if (arg_index >= 4) { + break; + } + + if (local_symbols[i].is_floating && xreg) { + + /* + * Microsoft x64 / EFI floating parameters arrive in XMM0-XMM3. + * Home only the ABI boundary value here; the rest of the backend + * can continue to load the parameter normally from its [rbp+N] + * slot and use x87 internally. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (local_symbols[i].size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " movss dword [rbp + %d], %s\n" : " movss dword ptr [rbp + %d], %s\n", offset, xreg); + } else { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " movsd qword [rbp + %d], %s\n" : " movsd qword ptr [rbp + %d], %s\n", offset, xreg); + } + + } else { + + if (local_symbols[i].size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, " movss %%%s, %d(%%rbp)\n", xreg, offset); + } else { + fprintf (state->ofp, " movsd %%%s, %d(%%rbp)\n", xreg, offset); + } + + } + + continue; + + } + + if (ireg) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov qword [rbp + %d], %s\n" : " mov qword ptr [rbp + %d], %s\n", offset, ireg); + } else { + fprintf (state->ofp, " movq %%%s, %d(%%rbp)\n", ireg, offset); + } + + } + + } + + /* + * For Microsoft x64 varargs, va_start/va_arg walks the register home + * area. The caller reserves that area but does not store the register + * values there, so the callee must home the register arguments that may + * be reached through the ellipsis. Do not overwrite fixed floating + * parameters already homed above; start after the declared fixed + * parameters. + */ + if (current_function_is_variadic) { + + for (i = local_symbol_count + hidden; i < 4; i++) { + + const char *ireg = amd64_integer_arg_reg (i); + int offset = 16 + i * 8; + + if (!ireg) { + continue; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov qword [rbp + %d], %s\n" : " mov qword ptr [rbp + %d], %s\n", offset, ireg); + } else { + fprintf (state->ofp, " movq %%%s, %d(%%rbp)\n", ireg, offset); + } + + } + + } + +} + +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) { + + masm_flush_data_line (); + + 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 rbp\n"); + fprintf (state->ofp, " mov rbp, rsp\n"); + + amd64_temp_stack_mod16 = 0; + + emit_home_incoming_parameter_regs (); + emit_deferred_function_frame_placeholder (); + + } else { + + fprintf (state->ofp, "%s:\n", asm_name); + fprintf (state->ofp, " pushq %%rbp\n"); + fprintf (state->ofp, " movq %%rsp, %%rbp\n"); + + emit_home_incoming_parameter_regs (); + emit_deferred_function_frame_placeholder (); + + } + +} + +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 rsp, %ld\n", allocate ? "sub" : "add", bytes); + } else { + fprintf (state->ofp, " %s $%ld, %%rsp\n", allocate ? "subq" : "addq", bytes); + } + +} + +static void emit_deferred_function_frame_placeholder (void) { + + current_function_frame_label = -1; + current_function_frame_deferred = 0; + + if (!state->ofp || !current_function_frame_enabled) { + return; + } + + current_function_frame_label = next_function_frame_label++; + current_function_frame_deferred = 1; + current_function_uses_single_frame = 1; + + fprintf (state->ofp, "__SCC_FRAME_PLACEHOLDER__\n"); + +} + +static void patch_deferred_function_frame (void) { + + current_function_frame_deferred = 0; + current_function_frame_label = -1; + current_function_frame_enabled = 0; + +} + +static void format_intel_rbp_offset (char *buf, size_t bufsz, long offset) { + + if (!buf || bufsz == 0) { + return; + } + + if (offset < 0) { + sprintf (buf, "[rbp - %ld]", -offset); + } else if (offset > 0) { + sprintf (buf, "[rbp + %ld]", offset); + } else { + sprintf (buf, "[rbp]"); + } + +} + +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[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_rbp_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)) { + + fprintf (state->ofp, " mov rax, 0%08lX%08lXh\n", high & U32_MASK, low & U32_MASK); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword %s, rax\n" : " mov qword ptr %s, rax\n"), memref); + + } 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(%%rbp)\n", low & 0xffUL, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw $%lu, %ld(%%rbp)\n", low & 0xffffUL, offset); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + fprintf (state->ofp, " movabsq $0x%08lX%08lX, %%rax\n", high & U32_MASK, low & U32_MASK); + fprintf (state->ofp, " movq %%rax, %ld(%%rbp)\n", offset); + + } else { + fprintf (state->ofp, " movl $%lu, %ld(%%rbp)\n", low & U32_MASK, offset); + } + + } + +} + +static void emit_store_floating_to_local_now (long offset, int size) { + + char memref[64]; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref, sizeof (memref), 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, " fstp%s %ld(%%rbp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", 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_rbp_offset (memref, sizeof (memref), offset); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov rax, %s\n", asm_symbol); + fprintf (state->ofp, " mov qword %s, rax\n", memref); + + } else { + + fprintf (state->ofp, " mov rax, offset %s\n", asm_symbol); + fprintf (state->ofp, " mov qword ptr %s, rax\n", memref); + + } + + } else { + + fprintf (state->ofp, " leaq %s(%%rip), %%rax\n", asm_symbol); + fprintf (state->ofp, " movq %%rax, %ld(%%rbp)\n", 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_rbp_offset (dst, sizeof (dst), dst_offset); + format_intel_rbp_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, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, al\n" : " 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 ? " movzx 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 if (src_size == (DATA_LLONG & 0x1f) || src_size == (DATA_PTR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov rax, qword %s\n" : " mov rax, qword 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); + } + + if (dst_size == (DATA_LLONG & 0x1f) || dst_size == (DATA_PTR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword %s, rax\n" : " mov qword ptr %s, rax\n"), dst); + } else { + 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(%%rbp), %%al\n", src_offset); + fprintf (state->ofp, " movb %%al, %ld(%%rbp)\n", dst_offset); + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + fprintf (state->ofp, " movw %ld(%%rbp), %%ax\n", src_offset); + fprintf (state->ofp, " movw %%ax, %ld(%%rbp)\n", dst_offset); + + } else { + + if (src_size == (DATA_LLONG & 0x1f) || src_size == (DATA_PTR & 0x1f)) { + fprintf (state->ofp, " movq %ld(%%rbp), %%rax\n", src_offset); + } else { + fprintf (state->ofp, " movl %ld(%%rbp), %%eax\n", src_offset); + } + + if (dst_size == (DATA_LLONG & 0x1f) || dst_size == (DATA_PTR & 0x1f)) { + fprintf (state->ofp, " movq %%rax, %ld(%%rbp)\n", dst_offset); + } else { + fprintf (state->ofp, " movl %%eax, %ld(%%rbp)\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_rbp_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 (dst_size == (DATA_LLONG & 0x1f) || dst_size == (DATA_PTR & 0x1f) || dst_size == (DATA_DOUBLE & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov rax, qword [%s]\n", asm_symbol); + fprintf (state->ofp, " mov qword %s, rax\n", dst); + + } else { + + fprintf (state->ofp, " mov rax, qword ptr %s\n", asm_symbol); + fprintf (state->ofp, " mov qword ptr %s, rax\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(%%rip), %%al\n", asm_symbol); + fprintf (state->ofp, " movb %%al, %ld(%%rbp)\n", dst_offset); + + } else if (dst_size == (DATA_SHORT & 0x1f)) { + + fprintf (state->ofp, " movw %s(%%rip), %%ax\n", asm_symbol); + fprintf (state->ofp, " movw %%ax, %ld(%%rbp)\n", dst_offset); + + } else if (dst_size == (DATA_LLONG & 0x1f) || dst_size == (DATA_PTR & 0x1f) || dst_size == (DATA_DOUBLE & 0x1f)) { + + fprintf (state->ofp, " movq %s(%%rip), %%rax\n", asm_symbol); + fprintf (state->ofp, " movq %%rax, %ld(%%rbp)\n", dst_offset); + + } else { + + fprintf (state->ofp, " movl %s(%%rip), %%eax\n", asm_symbol); + fprintf (state->ofp, " movl %%eax, %ld(%%rbp)\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) { + + patch_deferred_function_frame (); + + 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 rax, qword [rbp + 16]\n"); + } else { + fprintf (state->ofp, " mov rax, qword ptr [rbp + 16]\n"); + } + + } + + if (current_function_preserve_assignment64_regs) { + fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\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 rax, qword ptr [rbp + 16]\n"); + } else { + fprintf (state->ofp, " movq 16(%%rbp), %%rax\n"); + } + + } + + if (current_function_preserve_assignment64_regs) { + fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"); + } + + fprintf (state->ofp, " leave\n"); + fprintf (state->ofp, " ret\n"); + + } + +} + +static void emit_preserve_assignment64_regs (enum token_kind op) { + + if (current_function_frame_deferred) { + + int needed = (op == TOK_STAREQ || op == TOK_STAR || + op == TOK_SLASHEQ || op == TOK_BSLASH || + op == TOK_MODEQ || op == TOK_MOD) ? 2 : 1; + + if (needed > current_function_preserve_assignment64_regs) { + current_function_preserve_assignment64_regs = needed; + } + + return; + + } + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " push rbx\n"); + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " push rsi\n"); + fprintf (state->ofp, " push rdi\n"); + + } + + } else { + + fprintf (state->ofp, " pushq %%rbx\n"); + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " pushq %%rsi\n"); + fprintf (state->ofp, " pushq %%rdi\n"); + + } + + } + +} + +static void emit_restore_assignment64_regs (enum token_kind op) { + + if (current_function_frame_deferred) { + return; + } + + 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 rdi\n"); + fprintf (state->ofp, " pop rsi\n"); + + } + + fprintf (state->ofp, " pop rbx\n"); + + } else { + + if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) { + + fprintf (state->ofp, " popq %%rdi\n"); + fprintf (state->ofp, " popq %%rsi\n"); + + } + + fprintf (state->ofp, " popq %%rbx\n"); + + } + +} + +static int sizeof_text_skip_space_now (const char **pp) { + + const char *p; + + if (!pp || !*pp) { + return 0; + } + + p = *pp; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + *pp = p; + return 1; + +} + +static int sizeof_text_simple_identifier_now (const char *p, char *name, size_t name_size, int *parenthesized) { + + const char *q; + int paren = 0; + size_t n = 0; + + if (!p || !name || name_size == 0) { + return 0; + } + + name[0] = 0; + + if (parenthesized) { + *parenthesized = 0; + } + + sizeof_text_skip_space_now (&p); + + if (strncmp (p, "sizeof", 6) != 0 || + ((p[6] >= 'A' && p[6] <= 'Z') || (p[6] >= 'a' && p[6] <= 'z') || + (p[6] >= '0' && p[6] <= '9') || p[6] == '_')) { + return 0; + } + + p += 6; + sizeof_text_skip_space_now (&p); + + if (*p == '(') { + + paren = 1; + p++; + + sizeof_text_skip_space_now (&p); + + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + q = p; + + while ((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || + (*q >= '0' && *q <= '9') || *q == '_') { + + if (n + 1 < name_size) { + name[n++] = *q; + } + + q++; + + } + + name[n] = 0; + p = q; + + sizeof_text_skip_space_now (&p); + + if (paren) { + + if (*p != ')') { + return 0; + } + + p++; + sizeof_text_skip_space_now (&p); + + } + + if (!(*p == 0 || *p == ';' || *p == ',' || *p == ')' || *p == ']' || *p == '}' || + *p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' || + *p == '<' || *p == '>' || *p == '=' || *p == '?' || *p == ':' || + *p == '&' || *p == '|' || *p == '^')) { + return 0; + } + + if (parenthesized) { + *parenthesized = paren; + } + + return name[0] != 0; + +} + +static int sizeof_symbol_object_now (const char *name, int *sizep) { + + struct local_symbol *sym; + int global_index; + int size; + + if (!name || !*name || !sizep) { + return 0; + } + + sym = find_local_symbol (name); + + if (sym) { + + size = sym->size; + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + *sizep = size; + return 1; + + } + + global_index = find_global_symbol (name); + + if (global_index >= 0 && get_global_symbol_kind (name) != GLOBAL_SYMBOL_FUNCTION) { + + size = get_global_symbol_size (name); + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + *sizep = size; + return 1; + + } + + return 0; + +} + +static void sizeof_consume_simple_identifier_now (int parenthesized) { + + get_token (); + + if (parenthesized && tok.kind == TOK_LPAREN) { + get_token (); + } + + if (tok.kind == TOK_IDENT) { + get_token (); + } + + if (parenthesized && tok.kind == TOK_RPAREN) { + get_token (); + } + +} + +static int64_s sizeof_from_current_token (void) { + + int64_s v; + int size; + + char name[256]; + int parenthesized; + + zext64 (&v, 0); + + /* + * parse_sizeof_value() handles sizeof(type), but the backend also uses + * sizeof while parsing normal expressions after macro expansion. In that + * path, sizeof(object) must be accepted as an object-size expression, not + * routed into expr_const64() as if the identifier inside the parentheses + * were an integer constant expression. + */ + if (sizeof_text_simple_identifier_now (tok.start, name, sizeof (name), &parenthesized) && sizeof_symbol_object_now (name, &size)) { + + sizeof_consume_simple_identifier_now (parenthesized); + + zext64 (&v, (unsigned long) size); + return v; + + } + + 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_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_array_element_size_now (int base_size) { + + int object_size; + + if (!declarator_has_array) { + return 0; + } + + if (declarator_is_pointer) { + return DATA_PTR; + } + + /* + * For an array object, the element reached by one subscript is the + * complete row after the first dimension, not always the scalar base + * type. For example, with: + * + * static const char wday_name[7][3]; + * + * wday_name[i] has type char [3] and decays to the address of that + * three-byte row when passed to %.3s. Recording element size as char + * made expression code emit a byte load from wday_name + i instead of + * the row address wday_name + i * 3. + */ + if (declarator_array_dimensions > 1 && declarator_first_array_count > 0) { + + object_size = declarator_object_size (base_size); + + if (object_size > 0) { + return object_size / declarator_first_array_count; + } + + } + + /* + * For a one-dimensional array, the size of the element reached by one + * subscript is the declared base object size, not the total flattened + * initializer field size. make_declarator_fields() expands objects like + * + * char parmbuf[410]; + * + * into 410 byte fields for initialization/copying purposes. Feeding that + * field total back here made parmbuf[i] scale by 410 and store a dword far + * beyond the stack object. + */ + if (parsed_type_is_aggregate) { + return base_size; + } + + return base_size & 0x1f; + +} + +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 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 amd64_emit_load_assignment_rhs_expression_to_rax64 (int is_unsigned); + +static void emit_store_rax_to_local64 (long offset); +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 current_expression_mentions_64bit_symbol_now (void); +static int rhs_current_operand_is_unsigned_now (void); + +static int64_s parse_floating_const_expr_bits_now (int size); + +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, int object_size, int object_is_floating) { + + 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, object_size, object_is_floating); + + 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; + + } + + } + + if (object_is_floating) { + init->value = parse_floating_const_expr_bits_now (object_size); + } else { + 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], 0, 0); + (*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 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(%rbp) 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 (current_function_frame_deferred) { + + *block_stack_bytes = needed_stack_bytes; + *block_stack_emitted = (needed_stack_bytes > 0); + + 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 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]; + + int declaration_dllimport = parsed_dllimport; + + for (i = 0; i < MAX_AGG_FIELDS; i++) { + init_symbols[i] = 0; + } + + parse_declarator (&name); + + declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; + 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, + (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, + object_fields, object_field_count); + + } else if (name && (parsed_storage_class == STORAGE_EXTERN || declaration_dllimport)) { + + add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + set_global_symbol_size (name, (declarator_has_function && !declarator_is_pointer && !declarator_function_is_pointer) ? parsed_type_size : ((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 && declarator_function_is_pointer)) ? 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); + + set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, declaration_dllimport); + + copy_pending_params_to_global_symbol (name); + + } + + set_global_symbol_dllimport (name, declaration_dllimport); + 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(%rbp) before any sub rsp 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + 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 & 0x1f); + 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(%rbp), -12(%rbp), 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(%rbp) 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 || parsed_dllimport)) { + + 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], elem_size, 0); + } + + 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; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_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_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && is_string_token ()) { + + int first_init_index = init_count; + int value_count = 0; + + int64_s values[MAX_STRING_INIT_BYTES]; + int i; + + parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); + + if (declarator_array_unsized && value_count > 0 && value_count > object_size) { + + long old_offset = object_offset; + long new_offset; + long delta; + + int adj_i; + current_local_stack_size += value_count - object_size; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_size; + } + + new_offset = -current_local_stack_size; + delta = new_offset - old_offset; + object_offset = new_offset; + declarator_array_count = value_count; + + 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; + } + + } + + for (i = 0; i < object_size; i++) { + + if (init_count >= MAX_LOCAL_INITS) { + continue; + } + + inits[init_count].offset = object_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++; + + } + + } 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 ("rdx", object_offset); + + if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", 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 ("rax"); + + } else if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_has_array) { + + amd64_emit_load_assignment_rhs_expression_to_rax64 (parsed_type_is_unsigned); + emit_store_rax_to_local64 (object_offset); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_store_reg_to_local (object_offset, object_init_size, "rax"); + + } + + } + + } else if (!declarator_is_pointer && parsed_type_is_floating) { + + emit_load_floating_rhs_expression_now (object_init_size); + emit_store_floating_to_local_now (object_offset, object_init_size); + + } else if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_is_pointer) { + + amd64_emit_load_assignment_rhs_expression_to_rax64 (parsed_type_is_unsigned); + emit_store_rax_to_local64 (object_offset); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_store_reg_to_local (object_offset, object_init_size, "rax"); + + } + + } 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], object_init_size, (!declarator_is_pointer && parsed_type_is_floating)); + } + + 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_dimensions (static_label, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (static_label, declarator_array_element_size_now (parsed_type_size)); + + 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + 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); + } + + } + + } + + 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (block_stack_bytes, 1); + } + + if (!is_function_body && !current_function_frame_deferred) { + 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (extra_stack_bytes, 1); + } + + if (!is_function_body && !current_function_frame_deferred) { + 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 rsp, ...` 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (block_stack_bytes, 1); + } + + block_stack_emitted = (block_stack_bytes > 0); + + } else if (needed_stack_bytes > block_stack_bytes) { + + if (!current_function_frame_deferred) { + 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 && !current_function_frame_deferred && 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 int amd64_reg_is_qword_name (const char *reg) { + + if (!reg) { + return 0; + } + + return strcmp (reg, "rax") == 0 || strcmp (reg, "rbx") == 0 || + strcmp (reg, "rcx") == 0 || strcmp (reg, "rdx") == 0 || + strcmp (reg, "rsi") == 0 || strcmp (reg, "rdi") == 0 || + strcmp (reg, "r8") == 0 || strcmp (reg, "r9") == 0 || + strcmp (reg, "r10") == 0 || strcmp (reg, "r11") == 0 || + strcmp (reg, "r12") == 0 || strcmp (reg, "r13") == 0 || + strcmp (reg, "r14") == 0 || strcmp (reg, "r15") == 0; + +} + +static const char *amd64_qword_reg_name_from_any (const char *reg) { + + if (!reg) { + return reg; + } + + if (strcmp (reg, "rax") == 0 || strcmp (reg, "eax") == 0) return "rax"; + if (strcmp (reg, "rbx") == 0 || strcmp (reg, "ebx") == 0) return "rbx"; + if (strcmp (reg, "rcx") == 0 || strcmp (reg, "ecx") == 0) return "rcx"; + if (strcmp (reg, "rdx") == 0 || strcmp (reg, "edx") == 0) return "rdx"; + if (strcmp (reg, "rsi") == 0 || strcmp (reg, "esi") == 0) return "rsi"; + if (strcmp (reg, "rdi") == 0 || strcmp (reg, "edi") == 0) return "rdi"; + if (strcmp (reg, "r8") == 0 || strcmp (reg, "r8d") == 0) return "r8"; + if (strcmp (reg, "r9") == 0 || strcmp (reg, "r9d") == 0) return "r9"; + if (strcmp (reg, "r10") == 0 || strcmp (reg, "r10d") == 0) return "r10"; + if (strcmp (reg, "r11") == 0 || strcmp (reg, "r11d") == 0) return "r11"; + if (strcmp (reg, "r12") == 0 || strcmp (reg, "r12d") == 0) return "r12"; + if (strcmp (reg, "r13") == 0 || strcmp (reg, "r13d") == 0) return "r13"; + if (strcmp (reg, "r14") == 0 || strcmp (reg, "r14d") == 0) return "r14"; + if (strcmp (reg, "r15") == 0 || strcmp (reg, "r15d") == 0) return "r15"; + + return reg; + +} + +static const char *amd64_dword_reg_name_from_any (const char *reg) { + + if (!reg) { + return reg; + } + + if (strcmp (reg, "rax") == 0) return "eax"; + if (strcmp (reg, "rbx") == 0) return "ebx"; + if (strcmp (reg, "rcx") == 0) return "ecx"; + if (strcmp (reg, "rdx") == 0) return "edx"; + if (strcmp (reg, "rsi") == 0) return "esi"; + if (strcmp (reg, "rdi") == 0) return "edi"; + if (strcmp (reg, "r8") == 0) return "r8d"; + if (strcmp (reg, "r9") == 0) return "r9d"; + if (strcmp (reg, "r10") == 0) return "r10d"; + if (strcmp (reg, "r11") == 0) return "r11d"; + if (strcmp (reg, "r12") == 0) return "r12d"; + if (strcmp (reg, "r13") == 0) return "r13d"; + if (strcmp (reg, "r14") == 0) return "r14d"; + if (strcmp (reg, "r15") == 0) return "r15d"; + + return reg; + +} + +static const char *amd64_word_reg_name_from_any (const char *reg) { + + if (!reg) { + return reg; + } + + if (strcmp (reg, "rax") == 0 || strcmp (reg, "eax") == 0) return "ax"; + if (strcmp (reg, "rbx") == 0 || strcmp (reg, "ebx") == 0) return "bx"; + if (strcmp (reg, "rcx") == 0 || strcmp (reg, "ecx") == 0) return "cx"; + if (strcmp (reg, "rdx") == 0 || strcmp (reg, "edx") == 0) return "dx"; + if (strcmp (reg, "rsi") == 0 || strcmp (reg, "esi") == 0) return "si"; + if (strcmp (reg, "rdi") == 0 || strcmp (reg, "edi") == 0) return "di"; + if (strcmp (reg, "r8") == 0 || strcmp (reg, "r8d") == 0) return "r8w"; + if (strcmp (reg, "r9") == 0 || strcmp (reg, "r9d") == 0) return "r9w"; + if (strcmp (reg, "r10") == 0 || strcmp (reg, "r10d") == 0) return "r10w"; + if (strcmp (reg, "r11") == 0 || strcmp (reg, "r11d") == 0) return "r11w"; + if (strcmp (reg, "r12") == 0 || strcmp (reg, "r12d") == 0) return "r12w"; + if (strcmp (reg, "r13") == 0 || strcmp (reg, "r13d") == 0) return "r13w"; + if (strcmp (reg, "r14") == 0 || strcmp (reg, "r14d") == 0) return "r14w"; + if (strcmp (reg, "r15") == 0 || strcmp (reg, "r15d") == 0) return "r15w"; + + return reg; + +} + +static const char *amd64_byte_reg_name_from_any (const char *reg) { + + if (!reg) { + return reg; + } + + if (strcmp (reg, "rax") == 0 || strcmp (reg, "eax") == 0 || strcmp (reg, "ax") == 0) return "al"; + if (strcmp (reg, "rbx") == 0 || strcmp (reg, "ebx") == 0 || strcmp (reg, "bx") == 0) return "bl"; + if (strcmp (reg, "rcx") == 0 || strcmp (reg, "ecx") == 0 || strcmp (reg, "cx") == 0) return "cl"; + if (strcmp (reg, "rdx") == 0 || strcmp (reg, "edx") == 0 || strcmp (reg, "dx") == 0) return "dl"; + if (strcmp (reg, "rsi") == 0 || strcmp (reg, "esi") == 0 || strcmp (reg, "si") == 0) return "sil"; + if (strcmp (reg, "rdi") == 0 || strcmp (reg, "edi") == 0 || strcmp (reg, "di") == 0) return "dil"; + if (strcmp (reg, "r8") == 0 || strcmp (reg, "r8d") == 0 || strcmp (reg, "r8w") == 0) return "r8b"; + if (strcmp (reg, "r9") == 0 || strcmp (reg, "r9d") == 0 || strcmp (reg, "r9w") == 0) return "r9b"; + if (strcmp (reg, "r10") == 0 || strcmp (reg, "r10d") == 0 || strcmp (reg, "r10w") == 0) return "r10b"; + if (strcmp (reg, "r11") == 0 || strcmp (reg, "r11d") == 0 || strcmp (reg, "r11w") == 0) return "r11b"; + if (strcmp (reg, "r12") == 0 || strcmp (reg, "r12d") == 0 || strcmp (reg, "r12w") == 0) return "r12b"; + if (strcmp (reg, "r13") == 0 || strcmp (reg, "r13d") == 0 || strcmp (reg, "r13w") == 0) return "r13b"; + if (strcmp (reg, "r14") == 0 || strcmp (reg, "r14d") == 0 || strcmp (reg, "r14w") == 0) return "r14b"; + if (strcmp (reg, "r15") == 0 || strcmp (reg, "r15d") == 0 || strcmp (reg, "r15w") == 0) return "r15b"; + + return reg; + +} + +static int amd64_scalar_size_is_qword (int size) { + return size == (DATA_PTR & 0x1f); +} + +static void emit_load_local_to_reg_ex (const char *reg, long offset, int size, int is_unsigned) { + + const char *dreg; + char memref[64]; + + size &= 0x1f; + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref, sizeof (memref), offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, byte %s\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, memref); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word %s\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, memref); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword %s\n" : " mov %s, qword ptr %s\n"), amd64_qword_reg_name_from_any (reg), memref); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + + dreg = amd64_dword_reg_name_from_any (reg); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), dreg, memref); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsxd %s, dword %s\n" : " movsxd %s, dword 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)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s %ld(%%rbp), %%%s\n", is_unsigned ? "movzbl" : "movsbq", offset, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s %ld(%%rbp), %%%s\n", is_unsigned ? "movzbl" : "movsbl", offset, reg); + } + + } else if (size == (DATA_SHORT & 0x1f)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s %ld(%%rbp), %%%s\n", is_unsigned ? "movzwl" : "movswq", offset, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s %ld(%%rbp), %%%s\n", is_unsigned ? "movzwl" : "movswl", offset, reg); + } + + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " movq %ld(%%rbp), %%%s\n", offset, amd64_qword_reg_name_from_any (reg)); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + fprintf (state->ofp, " movl %ld(%%rbp), %%%s\n", offset, amd64_dword_reg_name_from_any (reg)); + } else { + fprintf (state->ofp, " movslq %ld(%%rbp), %%%s\n", offset, reg); + } + + } else { + fprintf (state->ofp, " movl %ld(%%rbp), %%%s\n", offset, reg); + } + + } + +} + +static void emit_load_local_to_reg (const char *reg, long offset, int size) { + emit_load_local_to_reg_ex (reg, offset, size, 0); +} + +static void emit_load_global_to_reg_ex (const char *reg, const char *symbol, int size, int is_unsigned) { + + const char *asm_symbol, *dreg; + size &= 0x1f; + + if (!state->ofp || !reg || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, size); + + if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { + + asm_symbol = asm_global_import_symbol_name (symbol); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr %s\n"), amd64_qword_reg_name_from_any (reg), asm_symbol); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, byte [%s]\n" : " %s %s, byte ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, amd64_qword_reg_name_from_any (reg)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, amd64_qword_reg_name_from_any (reg)); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr [%s]\n"), amd64_qword_reg_name_from_any (reg), amd64_qword_reg_name_from_any (reg)); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + + dreg = amd64_dword_reg_name_from_any (reg); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr [%s]\n"), dreg, amd64_qword_reg_name_from_any (reg)); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsxd %s, dword [%s]\n" : " movsxd %s, dword ptr [%s]\n"), reg, amd64_qword_reg_name_from_any (reg)); + } + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr [%s]\n"), reg, amd64_qword_reg_name_from_any (reg)); + } + + } else { + + fprintf (state->ofp, " movq %s(%%rip), %%%s\n", asm_symbol, amd64_qword_reg_name_from_any (reg)); + + if (size == (DATA_CHAR & 0x1f)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbq", reg, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", reg, reg); + } + + } else if (size == (DATA_SHORT & 0x1f)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswq", reg, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswl", reg, reg); + } + + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " movq (%%%s), %%%s\n", reg, amd64_qword_reg_name_from_any (reg)); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, amd64_dword_reg_name_from_any (reg)); + } else { + fprintf (state->ofp, " movslq (%%%s), %%%s\n", reg, reg); + } + + } else { + fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, 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 ? " %s %s, byte [%s]\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, asm_symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", (is_unsigned && amd64_reg_is_qword_name (reg)) ? amd64_dword_reg_name_from_any (reg) : reg, asm_symbol); + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr %s\n"), amd64_qword_reg_name_from_any (reg), asm_symbol); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + + dreg = amd64_dword_reg_name_from_any (reg); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr %s\n"), dreg, asm_symbol); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsxd %s, dword [%s]\n" : " movsxd %s, dword 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)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s %s(%%rip), %%%s\n", is_unsigned ? "movzbl" : "movsbq", asm_symbol, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s %s(%%rip), %%%s\n", is_unsigned ? "movzbl" : "movsbl", asm_symbol, reg); + } + + } else if (size == (DATA_SHORT & 0x1f)) { + + if (amd64_reg_is_qword_name (reg)) { + fprintf (state->ofp, " %s %s(%%rip), %%%s\n", is_unsigned ? "movzwl" : "movswq", asm_symbol, is_unsigned ? amd64_dword_reg_name_from_any (reg) : reg); + } else { + fprintf (state->ofp, " %s %s(%%rip), %%%s\n", is_unsigned ? "movzwl" : "movswl", asm_symbol, reg); + } + + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + fprintf (state->ofp, " movq %s(%%rip), %%%s\n", asm_symbol, amd64_qword_reg_name_from_any (reg)); + } else if (amd64_reg_is_qword_name (reg)) { + + if (is_unsigned) { + fprintf (state->ofp, " movl %s(%%rip), %%%s\n", asm_symbol, amd64_dword_reg_name_from_any (reg)); + } else { + fprintf (state->ofp, " movslq %s(%%rip), %%%s\n", asm_symbol, reg); + } + + } else { + fprintf (state->ofp, " movl %s(%%rip), %%%s\n", asm_symbol, reg); + } + + } + +} + +static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) { + emit_load_global_to_reg_ex (reg, symbol, size, 0); +} + +static void emit_store_reg_to_local (long offset, int size, const char *reg) { + + char memref[64]; + + const char *breg; + const char *wreg; + const char *dreg; + const char *qreg; + + if (!state->ofp || !reg) { + return; + } + + breg = amd64_byte_reg_name_from_any (reg); + wreg = amd64_word_reg_name_from_any (reg); + dreg = amd64_dword_reg_name_from_any (reg); + qreg = amd64_qword_reg_name_from_any (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref, sizeof (memref), offset); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, %s\n" : " mov byte ptr %s, %s\n"), memref, breg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, %s\n" : " mov word ptr %s, %s\n"), memref, wreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword %s, %s\n" : " mov qword ptr %s, %s\n"), memref, qreg); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref, dreg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%s, %ld(%%rbp)\n", breg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%s, %ld(%%rbp)\n", wreg, offset); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq %%%s, %ld(%%rbp)\n", qreg, offset); + } else { + fprintf (state->ofp, " movl %%%s, %ld(%%rbp)\n", dreg, offset); + } + + } + +} + +static void emit_store_reg_to_global (const char *symbol, int size, const char *reg) { + + const char *asm_symbol; + + const char *breg; + const char *wreg; + const char *dreg; + const char *qreg; + + if (!state->ofp || !symbol || !reg) { + return; + } + + breg = amd64_byte_reg_name_from_any (reg); + wreg = amd64_word_reg_name_from_any (reg); + dreg = amd64_dword_reg_name_from_any (reg); + qreg = amd64_qword_reg_name_from_any (reg); + + if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { + + const char *addr_reg = (strcmp (qreg, "rcx") == 0) ? "rdx" : "rcx"; + + asm_symbol = asm_global_import_symbol_name (symbol); + emit_extern_reference_symbol (symbol, size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr %s\n"), addr_reg, asm_symbol); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte [%s], %s\n" : " mov byte ptr [%s], %s\n"), addr_reg, breg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word [%s], %s\n" : " mov word ptr [%s], %s\n"), addr_reg, wreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword [%s], %s\n" : " mov qword ptr [%s], %s\n"), addr_reg, qreg); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr [%s], %s\n"), addr_reg, dreg); + } + + } else { + + fprintf (state->ofp, " movq %s(%%rip), %%%s\n", asm_symbol, addr_reg); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%s, (%%%s)\n", breg, addr_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%s, (%%%s)\n", wreg, addr_reg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq %%%s, (%%%s)\n", qreg, addr_reg); + } else { + fprintf (state->ofp, " movl %%%s, (%%%s)\n", dreg, addr_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], %s\n" : " mov byte ptr %s, %s\n"), asm_symbol, breg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word [%s], %s\n" : " mov word ptr %s, %s\n"), asm_symbol, wreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword [%s], %s\n" : " mov qword ptr %s, %s\n"), asm_symbol, qreg); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr %s, %s\n"), asm_symbol, dreg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%s, %s(%%rip)\n", breg, asm_symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%s, %s(%%rip)\n", wreg, asm_symbol); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq %%%s, %s(%%rip)\n", qreg, asm_symbol); + } else { + fprintf (state->ofp, " movl %%%s, %s(%%rip)\n", dreg, asm_symbol); + } + + } + +} + +static void emit_load_local64_to_pair (long offset, const char *lo, const char *hi) { + + const char *qlo; + char memref_lo[64], memref_hi[64]; + + if (!state->ofp || !lo || !hi) { + return; + } + + if ((strcmp (lo, "rax") == 0 && strcmp (hi, "rdx") == 0) || (strcmp (lo, "rbx") == 0 && strcmp (hi, "rcx") == 0) || (strcmp (lo, "rax") == 0 && strcmp (hi, "r10") == 0)) { + + qlo = (strcmp (lo, "rbx") == 0) ? "rbx" : "rax"; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref_lo, sizeof (memref_lo), offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword %s\n" : " mov %s, qword ptr %s\n"), qlo, memref_lo); + + } else { + fprintf (state->ofp, " movq %ld(%%rbp), %%%s\n", offset, qlo); + } + + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref_lo, sizeof (memref_lo), offset); + format_intel_rbp_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"), amd64_dword_reg_name_from_any (lo), memref_lo); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), amd64_dword_reg_name_from_any (hi), memref_hi); + + } else { + + fprintf (state->ofp, " movl %ld(%%rbp), %%%s\n", offset, amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " movl %ld(%%rbp), %%%s\n", offset + 4, amd64_dword_reg_name_from_any (hi)); + + } + +} + +static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi) { + + char memref_lo[64]; + + if (!state->ofp || !lo || !hi) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref_lo, sizeof (memref_lo), offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword %s, %s\n" : " mov qword ptr %s, %s\n"), memref_lo, amd64_qword_reg_name_from_any (lo)); + + } else { + fprintf (state->ofp, " movq %%%s, %ld(%%rbp)\n", amd64_qword_reg_name_from_any (lo), offset); + } + +} + +static void emit_store_rax_to_local64 (long offset) { + + char memref[64]; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref, sizeof (memref), offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword %s, rax\n" : " mov qword ptr %s, rax\n"), memref); + + } else { + fprintf (state->ofp, " movq %%rax, %ld(%%rbp)\n", offset); + } + +} + +static void emit_load_local64_to_rax (long offset) { + + char memref[64]; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_rbp_offset (memref, sizeof (memref), offset); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov rax, qword %s\n" : " mov rax, qword ptr %s\n"), memref); + + } else { + fprintf (state->ofp, " movq %ld(%%rbp), %%rax\n", offset); + } + +} + +static void emit_load_global64_to_rax (const char *symbol) { + + const char *asm_symbol; + + if (!state->ofp || !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 rax, qword [%s]\n" : " mov rax, qword ptr %s\n"), asm_symbol); + } else { + fprintf (state->ofp, " movq %s(%%rip), %%rax\n", asm_symbol); + } + +} + +static void emit_load_global64_to_pair (const char *lo, const char *hi, const char *symbol) { + + const char *qlo; + 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 ((strcmp (lo, "rax") == 0 && strcmp (hi, "rdx") == 0) || (strcmp (lo, "rbx") == 0 && strcmp (hi, "rcx") == 0) || (strcmp (lo, "rax") == 0 && strcmp (hi, "r10") == 0)) { + + qlo = (strcmp (lo, "rbx") == 0) ? "rbx" : "rax"; + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr %s\n"), qlo, asm_symbol); + } else { + fprintf (state->ofp, " movq %s(%%rip), %%%s\n", asm_symbol, qlo); + } + + return; + + } + + (void) hi; + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr %s\n"), amd64_qword_reg_name_from_any (lo), asm_symbol); + } else { + fprintf (state->ofp, " movq %s(%%rip), %%%s\n", asm_symbol, amd64_qword_reg_name_from_any (lo)); + } + +} + +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); + + (void) hi; + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword [%s], %s\n" : " mov qword ptr %s, %s\n"), asm_symbol, amd64_qword_reg_name_from_any (lo)); + } else { + fprintf (state->ofp, " movq %%%s, %s(%%rip)\n", amd64_qword_reg_name_from_any (lo), 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 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 >= 8) { + + chunk = 8; + offset -= 8; + + } else 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 == 8) { + fprintf (state->ofp, " push qword [%s + %d]\n", reg, offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword [%s + %d]\n", reg, offset); + fprintf (state->ofp, " mov qword [rsp], rax\n"); + + } else if (chunk == 2) { + + fprintf (state->ofp, " movzx edx, word [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push rdx\n"); + + } else { + + fprintf (state->ofp, " movzx edx, byte [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push rdx\n"); + + } + + } else { + + if (chunk == 8) { + fprintf (state->ofp, " push qword ptr [%s + %d]\n", reg, offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword ptr [%s + %d]\n", reg, offset); + fprintf (state->ofp, " mov qword ptr [rsp], rax\n"); + + } else if (chunk == 2) { + + fprintf (state->ofp, " movzx edx, word ptr [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push rdx\n"); + + } else { + + fprintf (state->ofp, " movzx edx, byte ptr [%s + %d]\n", reg, offset); + fprintf (state->ofp, " push rdx\n"); + + } + + } + + } else { + + if (chunk == 8) { + fprintf (state->ofp, " pushq %d(%%%s)\n", offset, reg); + } else if (chunk == 4) { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + + fprintf (state->ofp, " xorq %%rax, %%rax\n"); + fprintf (state->ofp, " movl %d(%%%s), %%eax\n", offset, reg); + fprintf (state->ofp, " movq %%rax, (%%rsp)\n"); + + } else if (chunk == 2) { + + fprintf (state->ofp, " movzwl %d(%%%s), %%edx\n", offset, reg); + fprintf (state->ofp, " pushq %%rdx\n"); + + } else { + + fprintf (state->ofp, " movzbl %d(%%%s), %%edx\n", offset, reg); + fprintf (state->ofp, " pushq %%rdx\n"); + + } + + } + + } + + return 1; + +} + +static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg); + +static void amd64_emit_sub_rsp_bytes (int bytes); +static void amd64_emit_add_rsp_bytes (int bytes); + +static void amd64_emit_store_rax_to_home_arg (int index); +static void amd64_emit_load_home_arg_to_reg (int index); + +static void amd64_emit_store_rax_to_stack_arg (int slot); +static void amd64_emit_move_rax_to_arg_reg (int index); +static void amd64_emit_move_st0_to_arg (int index, int size, int duplicate_gp); + +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 const char *amd64_qreg_name (const char *reg) { + + if (!reg) return reg; + if (strcmp (reg, "rax") == 0 || strcmp (reg, "eax") == 0) return "rax"; + if (strcmp (reg, "rbx") == 0 || strcmp (reg, "ebx") == 0) return "rbx"; + if (strcmp (reg, "rcx") == 0 || strcmp (reg, "ecx") == 0) return "rcx"; + if (strcmp (reg, "rdx") == 0 || strcmp (reg, "edx") == 0) return "rdx"; + if (strcmp (reg, "rsi") == 0 || strcmp (reg, "esi") == 0) return "rsi"; + if (strcmp (reg, "rdi") == 0 || strcmp (reg, "edi") == 0) return "rdi"; + if (strcmp (reg, "r8") == 0 || strcmp (reg, "r8d") == 0) return "r8"; + if (strcmp (reg, "r9") == 0 || strcmp (reg, "r9d") == 0) return "r9"; + if (strcmp (reg, "r10") == 0 || strcmp (reg, "r10d") == 0) return "r10"; + if (strcmp (reg, "r11") == 0 || strcmp (reg, "r11d") == 0) return "r11"; + if (strcmp (reg, "r12") == 0 || strcmp (reg, "r12d") == 0) return "r12"; + if (strcmp (reg, "r13") == 0 || strcmp (reg, "r13d") == 0) return "r13"; + if (strcmp (reg, "r14") == 0 || strcmp (reg, "r14d") == 0) return "r14"; + if (strcmp (reg, "r15") == 0 || strcmp (reg, "r15d") == 0) return "r15"; + return reg; + +} + +static void emit_push_reg_now (const char *reg) { + + reg = amd64_qreg_name (reg); + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " push %s\n", reg); + } else { + fprintf (state->ofp, " pushq %%%s\n", reg); + } + + amd64_note_stack_sub (DATA_PTR & 0x1f); + +} + +static void emit_pop_reg_now (const char *reg) { + + reg = amd64_qreg_name (reg); + + if (!state->ofp || !reg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " pop %s\n", reg); + } else { + fprintf (state->ofp, " popq %%%s\n", reg); + } + + amd64_note_stack_add (DATA_PTR & 0x1f); + +} + +static void emit_mov_reg_to_reg_now (const char *dst, const char *src) { + + dst = amd64_qreg_name (dst); + src = amd64_qreg_name (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, " movq %%%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, qword [%s + %s * 8]\n", base_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " mov %s, qword ptr [%s + %s * 8]\n", base_reg, base_reg, index_reg); + } + + } else { + fprintf (state->ofp, " movq (%%%s,%%%s,8), %%%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, int is_unsigned) { + + 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, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } + + } else { + + if (amd64_reg_is_qword_name (dst_reg)) { + fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbq", base_reg, index_reg, is_unsigned ? amd64_dword_reg_name_from_any (dst_reg) : dst_reg); + } else { + fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", base_reg, index_reg, dst_reg); + } + + } + +} + +static void emit_load_indexed_sized_to_reg_ex_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size, int is_unsigned) { + + int scale = 1; + + const char *gasop = is_unsigned ? "movzbl" : "movsbl"; + const char *atype = "byte"; + + if (!state->ofp || !base_reg || !index_reg || !dst_reg) { + return; + } + + elem_size &= 0x1f; + + if (elem_size == (DATA_SHORT & 0x1f)) { + + scale = 2; + + gasop = is_unsigned ? "movzwl" : "movswl"; + atype = "word"; + + } else if (elem_size == (DATA_INT & 0x1f) || elem_size == (DATA_LONG & 0x1f)) { + + scale = 4; + + gasop = is_unsigned ? "movl" : "movslq"; + atype = "dword"; + + } else if (elem_size == (DATA_LLONG & 0x1f) || elem_size == (DATA_PTR & 0x1f) || elem_size == (DATA_DOUBLE & 0x1f)) { + + scale = 8; + + atype = "qword"; + gasop = "movq"; + + } + + 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, is_unsigned); + } else if (scale == 1) { + + if (strcmp (atype, "byte") == 0) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } + + } else if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, word [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, word ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } + + } else if (strcmp (atype, "dword") == 0 && amd64_reg_is_qword_name (dst_reg)) { + + if (is_unsigned) { + + const char *dreg = amd64_dword_reg_name_from_any (dst_reg); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s + %s]\n" : " mov %s, dword ptr [%s + %s]\n"), dreg, base_reg, index_reg); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsxd %s, dword [%s + %s]\n" : " movsxd %s, dword 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 (strcmp (atype, "byte") == 0) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, byte [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } + + } else if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, word [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " %s %s, word ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } + + } else if (strcmp (atype, "dword") == 0 && amd64_reg_is_qword_name (dst_reg)) { + + if (is_unsigned) { + + const char *dreg = amd64_dword_reg_name_from_any (dst_reg); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s + %s * %d]\n" : " mov %s, dword ptr [%s + %s * %d]\n"), dreg, base_reg, index_reg, scale); + + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsxd %s, dword [%s + %s * %d]\n" : " movsxd %s, dword 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 { + + const char *att_dst_reg = dst_reg; + const char *att_op = gasop; + + if (amd64_reg_is_qword_name (dst_reg)) { + + if (elem_size == (DATA_CHAR & 0x1f)) { + + if (is_unsigned) { + + att_dst_reg = amd64_dword_reg_name_from_any (dst_reg); + att_op = "movzbl"; + + } else { + att_op = "movsbq"; + } + + } else if (elem_size == (DATA_SHORT & 0x1f)) { + + if (is_unsigned) { + + att_dst_reg = amd64_dword_reg_name_from_any (dst_reg); + att_op = "movzwl"; + + } else { + att_op = "movswq"; + } + + } else if (elem_size == (DATA_INT & 0x1f) || elem_size == (DATA_LONG & 0x1f)) { + + if (is_unsigned) { + + att_dst_reg = amd64_dword_reg_name_from_any (dst_reg); + att_op = "movl"; + + } else { + att_op = "movslq"; + } + + } + + } + + if (scale == 1) { + fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", att_op, base_reg, index_reg, att_dst_reg); + } else { + fprintf (state->ofp, " %s (%%%s,%%%s,%d), %%%s\n", att_op, base_reg, index_reg, scale, att_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) { + emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 0); +} + +static void emit_load_indexed_unsigned_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { + emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 1); +} + +static const char *amd64_full_reg_name (const char *reg) { + + if (!reg) { + return reg; + } + + if (strcmp (reg, "rax") == 0) { + return "rax"; + } + + if (strcmp (reg, "rbx") == 0) { + return "rbx"; + } + + if (strcmp (reg, "rcx") == 0) { + return "rcx"; + } + + if (strcmp (reg, "rdx") == 0) { + return "rdx"; + } + + if (strcmp (reg, "rsi") == 0) { + return "rsi"; + } + + if (strcmp (reg, "rdi") == 0) { + return "rdi"; + } + + if (strcmp (reg, "ebp") == 0) { + return "rbp"; + } + + if (strcmp (reg, "esp") == 0) { + return "rsp"; + } + + return 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) { + + const char *addr_reg = amd64_full_reg_name (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " lea %s, [rbp%+ld]\n", addr_reg, offset); + } else { + fprintf (state->ofp, " leaq %ld(%%rbp), %%%s\n", offset, addr_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) { + + const char *addr_reg = amd64_full_reg_name (reg); + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " lea %s, [%s]\n", addr_reg, asm_symbol); + } else { + fprintf (state->ofp, " lea %s, %s\n", addr_reg, asm_symbol); + } + + } else { + fprintf (state->ofp, " leaq %s(%%rip), %%%s\n", asm_symbol, amd64_full_reg_name (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_INT & 0x1f)) { + + elem_size = DATA_INT & 0x1f; + scale = 4; + + } else if (raw_elem_size == DATA_PTR || raw_elem_size == (DATA_PTR & 0x1f)) { + + elem_size = DATA_PTR & 0x1f; + scale = 8; + + } 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, " imulq $%d, %%%s, %%%s\n", elem_size, index_reg, index_reg); + fprintf (state->ofp, " leaq (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg); + + } else if (scale == 1) { + fprintf (state->ofp, " leaq (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg); + } else { + fprintf (state->ofp, " leaq (%%%s,%%%s,%d), %%%s\n", base_reg, index_reg, scale, base_reg); + } + + } + +} + +static void emit_load_subscript_index_to_reg_now (const char *index_reg) { + + if (current_expression_mentions_64bit_symbol_now ()) { + + int is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", is_unsigned); + + if (index_reg && strcmp (index_reg, "rax") != 0) { + emit_mov_reg_to_reg_now (index_reg, "rax"); + } + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (index_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 = "rax"; + } + + index_reg = (strcmp (reg, "rcx") == 0) ? "rdx" : "rcx"; + + while (tok.kind == TOK_LBRACK) { + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_subscript_index_to_reg_now (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 = "rax"; + } + + index_reg = (strcmp (reg, "rcx") == 0) ? "rdx" : "rcx"; + + while (tok.kind == TOK_LBRACK) { + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_subscript_index_to_reg_now (index_reg); + + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + + /* + * This is a real multidimensional array row, not an array of + * pointers. For char a[7][3], a[i] is the address of the + * three-byte row. The old code treated every further subscript + * as pointer traversal and loaded *(a + i), which turns the + * first bytes of the row into a bogus pointer. + */ + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + elem_size = index_step_size (pointed_size); + + } else { + + 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 (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { + 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 emit_parse_postfix_subscripts_to_reg_dims_now (const char *reg, int elem_size, int pointer_depth, int pointed_size, int array_dimensions, int is_unsigned) { + + const char *index_reg; + int saw_subscript = 0; + + if (!reg) { + reg = "rax"; + } + + index_reg = (strcmp (reg, "rcx") == 0) ? "rdx" : "rcx"; + + while (tok.kind == TOK_LBRACK) { + + int dims_before = array_dimensions; + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_subscript_index_to_reg_now (index_reg); + + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (array_dimensions > 0) { + array_dimensions--; + } + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + elem_size = index_step_size (pointed_size); + + } else { + + 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 (pointer_depth == 0 && dims_before > 1) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else { + + if (is_unsigned) { + emit_load_indexed_unsigned_sized_to_reg_now (reg, index_reg, 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 = "rax"; + } + + addr_reg = (strcmp (reg, "rdx") == 0) ? "rcx" : "rdx"; + + 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 ? " addq $%d, %%%s\n" : " subq $%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 = "rax"; + } + + addr_reg = (strcmp (reg, "rdx") == 0) ? "rcx" : "rdx"; + + 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 ? " addq $1, %%%s\n" : " subq $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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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, "rdx", 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"; + + const char *dreg = amd64_dword_reg_name_from_any (reg); + int qword = (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f)); + + if (size == (DATA_CHAR & 0x1f)) { + opsize = "byte"; + } else if (size == (DATA_SHORT & 0x1f)) { + opsize = "word"; + } else if (qword) { + opsize = "qword"; + } + + 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", dreg, reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word [%s + %d]\n", dreg, reg, offset); + } else if (qword) { + fprintf (state->ofp, " mov %s, qword [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " movsxd %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", dreg, reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", dreg, reg, offset); + } else if (qword) { + fprintf (state->ofp, " mov %s, qword ptr [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " movsxd %s, dword ptr [%s + %d]\n", reg, reg, offset); + } + + } + + } else { + + const char *suffix = "l"; + int qword = (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f)); + + if (size == (DATA_CHAR & 0x1f)) { + suffix = "b"; + } else if (size == (DATA_SHORT & 0x1f)) { + suffix = "w"; + } else if (qword) { + suffix = "q"; + } + + 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, amd64_dword_reg_name_from_any (reg)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, amd64_dword_reg_name_from_any (reg)); + } else if (qword) { + fprintf (state->ofp, " movq %d(%%%s), %%%s\n", offset, reg, reg); + } else { + fprintf (state->ofp, " movslq %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_ex (lo, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (lo, sym->offset, sym->size, sym->is_unsigned); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorq %%%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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (name)); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorq %%%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) { + return amd64_byte_reg_name_from_any (reg); +} + +static const char *reg16_name_for_32 (const char *reg) { + return amd64_word_reg_name_from_any (reg); +} + +static void emit_extend_pair_high_from_low (const char *lo, const char *hi, int size, int is_unsigned) { + + const char *dlo; + const char *dhi; + + if (!state->ofp) { + return; + } + + dlo = amd64_dword_reg_name_from_any (lo); + dhi = amd64_dword_reg_name_from_any (hi); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (is_unsigned) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzx %s, %s\n", dlo, reg8_name_for_32 (lo)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzx %s, %s\n", dlo, reg16_name_for_32 (lo)); + } + + fprintf (state->ofp, " xor %s, %s\n", dhi, dhi); + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsx %s, %s\n", dlo, reg8_name_for_32 (lo)); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %s, %s\n", dlo, reg16_name_for_32 (lo)); + } + + fprintf (state->ofp, " mov %s, %s\n", dhi, dlo); + fprintf (state->ofp, " sar %s, 31\n", dhi); + + } + + } else { + + if (is_unsigned) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movzbl %%%s, %%%s\n", reg8_name_for_32 (lo), dlo); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movzwl %%%s, %%%s\n", reg16_name_for_32 (lo), dlo); + } + + fprintf (state->ofp, " xorl %%%s, %%%s\n", dhi, dhi); + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsbl %%%s, %%%s\n", reg8_name_for_32 (lo), dlo); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movswl %%%s, %%%s\n", reg16_name_for_32 (lo), dlo); + } + + fprintf (state->ofp, " movl %%%s, %%%s\n", dlo, dhi); + fprintf (state->ofp, " sarl $31, %%%s\n", dhi); + + } + + } + +} + +static void emit_apply_integer_cast_to_reg_now (const char *reg, int size, int is_unsigned) { + + const char *dreg; + const char *qreg; + + if (!state->ofp) { + return; + } + + if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f)) { + return; + } + + dreg = amd64_dword_reg_name_from_any (reg); + qreg = amd64_qword_reg_name_from_any (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", is_unsigned ? dreg : qreg, reg8_name_for_32 (reg)); + } else { + fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", is_unsigned ? dreg : qreg, reg16_name_for_32 (reg)); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, is_unsigned ? " movzbl %%%s, %%%s\n" : " movsbq %%%s, %%%s\n", reg8_name_for_32 (reg), is_unsigned ? dreg : qreg); + } else { + fprintf (state->ofp, is_unsigned ? " movzwl %%%s, %%%s\n" : " movswq %%%s, %%%s\n", reg16_name_for_32 (reg), is_unsigned ? dreg : qreg); + } + + } + +} + +static int fold_text_starts_with_type_name_only_before_rparen (const char *p) { + + int saw_type = 0; + char word[64]; + int i; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '*') { + + saw_type = 1; + + p++; + continue; + + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + break; + } + + 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, "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_type = 1; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return saw_type && *p == ')'; + +} + +static int const_integer_expr_text_is_foldable_now (const char *p) { + + int saw_value_token = 0; + int saw_token = 0; + + char word[64]; + int depth = 0, ch, i; + + if (!p) { + return 0; + } + + { + + const char *q = p; + int parens = 0; + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + while (*q == '(') { + + parens = 1; + q++; + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + } + + if (fold_text_starts_with_type_name_only_before_rparen (p) || (parens && fold_text_starts_with_type_name_only_before_rparen (q))) { + 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) { + + /* + * If the caller asks about text that starts inside a cast, the + * scanner can see only the type-name prefix, for example: + * + * unsigned char) ch + * + * That is not an integer constant expression. Returning true + * here sends the parser down expr_const64(), which then reports + * "integer constant expression expected" at the cast. + */ + if (!saw_value_token) { + return 0; + } + + break; + + } + + depth--; + p++; + + continue; + + } + + if (*p >= '0' && *p <= '9') { + + saw_value_token = 1; + saw_token = 1; + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + continue; + + } + + if (*p == '\'') { + + saw_value_token = 1; + 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) { + saw_value_token = 1; + } + + 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 int token_kind_is_integer_constant_now (enum token_kind kind) { + + return kind == TOK_CCHAR || kind == TOK_LCHAR || + kind == TOK_CINT || kind == TOK_CUINT || + kind == TOK_CLONG || kind == TOK_CULONG || + kind == TOK_CLLONG || kind == TOK_CULLONG; + +} + +static int token_kind_is_binary_expression_operator_now (enum token_kind kind) { + + return kind == TOK_PLUS || kind == TOK_MINUS || kind == TOK_STAR || + kind == TOK_FSLASH || kind == TOK_MOD || + kind == TOK_AMPER || kind == TOK_PIPE || kind == TOK_CARET || + kind == TOK_LSH || kind == TOK_RSH || + kind == TOK_LESS || kind == TOK_LTEQ || + kind == TOK_GREATER || kind == TOK_GTEQ || + kind == TOK_EQEQ || kind == TOK_NOTEQ || + kind == TOK_LOGAND || kind == TOK_LOGOR; + +} + +static int const_integer_expr_text_contains_sizeof_now (const char *p) { + + char quote; + char word[64]; + int i; + + if (!p) { + return 0; + } + + while (*p) { + + if (*p == '\'' || *p == '"') { + + quote = *p++; + + while (*p) { + + if (*p == '\\') { + + p++; + + if (*p) { + p++; + } + + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + 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) { + return 1; + } + + continue; + + } + + p++; + + } + + return 0; + +} + +static int current_integer_expr_is_foldable_now (void) { + + struct token *saved_tok; + + enum token_kind first_kind; + enum token_kind next_kind; + + if (!const_integer_expr_text_is_foldable_now (tok.start)) { + return 0; + } + + /* + * The backend-side constant folder delegates to expr_const64(), and that + * parser does not understand the full sizeof-expression grammar. It is + * safe to fold a single leading sizeof through const64_from_current_operand(), + * but it is not safe to fold a whole expression such as: + * + * sizeof x < 8 ? 8 : sizeof x + * + * because a later sizeof token would be handed to expr_const64() and produce + * a bogus "integer constant expression expected" diagnostic. Leave these + * expressions to the normal expression emitter, which already has a real + * token-level sizeof path. + */ + if (const_integer_expr_text_contains_sizeof_now (tok.start)) { + return 0; + } + + /* + * Macro-expanded integer tokens can have tok.start/tok.caret pointing at + * only the replacement text (for example "16") rather than the complete + * source expression that follows it. Do not hand such a prefix to + * expr_const64() as though it described the whole expression when the real + * token stream continues with a binary operator: + * + * MEMMGR_ALIGN - (size_t)buffer % MEMMGR_ALIGN + * + * The normal expression parser can still handle this; this guard only + * disables the whole-expression constant-folder for that unsafe prefix. + */ + if (!token_kind_is_integer_constant_now (tok.kind)) { + return 1; + } + + first_kind = tok.kind; + + saved_tok = clone_current_token_now (); + get_token (); + + next_kind = tok.kind; + unget_token (saved_tok); + + if (token_kind_is_integer_constant_now (first_kind) && token_kind_is_binary_expression_operator_now (next_kind)) { + return 0; + } + + return 1; + +} + +static void amd64_emit_mov_i64_to_qword_reg_now (const char *reg, int64_s v) { + + const char *qreg = amd64_qword_reg_name_from_any (reg); + + if (!state->ofp || !qreg) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, 0%08lX%08lXh\n", qreg, v.high & U32_MASK, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movabsq $0x%08lX%08lX, %%%s\n", v.high & U32_MASK, v.low & U32_MASK, qreg); + } + +} + +static void emit_load_const64_to_pair_now (const char *lo, const char *hi, int64_s v) { + + const char *qlo = amd64_qword_reg_name_from_any (lo); + + if (!state->ofp || !lo) { + return; + } + + if (amd64_reg_is_qword_name (qlo)) { + + amd64_emit_mov_i64_to_qword_reg_now (qlo, v); + return; + + } + + { + + const char *dlo = amd64_dword_reg_name_from_any (lo); + const char *dhi = amd64_dword_reg_name_from_any (hi); + + if (!dlo || !dhi) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov %s, %lu\n", dlo, v.low & U32_MASK); + fprintf (state->ofp, " mov %s, %lu\n", dhi, v.high & U32_MASK); + + } else { + + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, dlo); + fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, dhi); + + } + + } + +} + +static void emit_load_const32_to_reg_now (const char *reg, int64_s v) { + + const char *dreg = amd64_dword_reg_name_from_any (reg); + flush_pending_statement_labels (); + + if (!state->ofp || !dreg) { + return; + } + + /* + * A 32-bit integer constant loaded through the dword register form + * zero-extends the upper half of the AMD64 register. Do not use a + * qword immediate move here: assemblers may choose a sign-extended + * imm32 encoding for values with bit 31 set, which is wrong for plain + * unsigned 32-bit constants promoted into a 64-bit register. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", dreg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, dreg); + } + +} + +static void emit_load_address_to_reg_now (const char *reg, const char *symbol) { + + const char *asm_symbol; + const char *addr_reg; + + flush_pending_statement_labels (); + + if (!state->ofp || !symbol || !*symbol) { + return; + } + + emit_extern_reference_symbol (symbol, DATA_PTR); + + asm_symbol = asm_global_symbol_name (symbol); + addr_reg = amd64_full_reg_name (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " lea %s, [%s]\n", addr_reg, asm_symbol); + } else { + fprintf (state->ofp, " mov %s, offset %s\n", addr_reg, asm_symbol); + } + + } else { + fprintf (state->ofp, " leaq %s(%%rip), %%%s\n", asm_symbol, addr_reg); + } + +} + +static void emit_load_deref_reg_ex_now (const char *reg, int size, int is_unsigned); +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_ex_now (const char *lo, const char *hi, int size, int is_unsigned) { + + 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) { + + const char *dhi = amd64_dword_reg_name_from_any (hi); + const char *qhi = amd64_qword_reg_name_from_any (hi); + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + 4]\n", dhi, qhi); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", dhi, qhi); + } + + } else { + fprintf (state->ofp, " movl 4(%%%s), %%%s\n", amd64_qword_reg_name_from_any (hi), amd64_dword_reg_name_from_any (hi)); + } + + } else { + + emit_load_deref_reg_ex_now (lo, size, is_unsigned); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorq %%%s, %%%s\n", hi, hi); + } + + } + +} + +static void emit_store_pair_to_deref_reg_now (const char *addr_reg, const char *lo, const char *hi) { + + (void) hi; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov qword [%s], %s\n", addr_reg, amd64_qword_reg_name_from_any (lo)); + } else { + fprintf (state->ofp, " mov qword ptr [%s], %s\n", addr_reg, amd64_qword_reg_name_from_any (lo)); + } + + } else { + fprintf (state->ofp, " movq %%%s, (%%%s)\n", amd64_qword_reg_name_from_any (lo), addr_reg); + } + +} + +static void emit_load_pair_from_deref_reg_now (const char *lo, const char *hi, const char *addr_reg) { + + (void) hi; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, qword [%s]\n", amd64_qword_reg_name_from_any (lo), addr_reg); + } else { + fprintf (state->ofp, " mov %s, qword ptr [%s]\n", amd64_qword_reg_name_from_any (lo), addr_reg); + } + + } else { + fprintf (state->ofp, " movq (%%%s), %%%s\n", addr_reg, amd64_qword_reg_name_from_any (lo)); + } + +} + +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, " movq %%%s, %%%s\n", src, dst); + } + +} + +static int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating); +static int rhs_current_operand_is_floating_now (void); +static int token_is_floating_constant_now (void); + +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 void emit_load_assignment_rhs_to_reg (const char *reg); +static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi); +static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size); + +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)) { + + if (!cast_is_pointer && !last_cast_type_is_floating && rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + + if (floating_rhs_result_in_eax_bool) { + + if (strcmp (lo, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", lo); + } else { + fprintf (state->ofp, " movq %%rax, %%%s\n", lo); + } + + } + + emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); + floating_rhs_result_in_eax_bool = 0; + + } else { + + emit_floating_stack_to_int_pair_now (lo, hi); + + if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { + emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); + } + + } + + return; + + } + + 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, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + + 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_LPAREN && current_integer_expr_is_foldable_now ()) { + + 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, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + + 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, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", lo); + } + + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } + + } else { + + if (unary_op == TOK_MINUS) { + + fprintf (state->ofp, " notq %%%s\n", lo); + fprintf (state->ofp, " notq %%%s\n", hi); + + fprintf (state->ofp, " addq $1, %%%s\n", lo); + fprintf (state->ofp, " adcq $0, %%%s\n", hi); + + } else if (unary_op == TOK_TILDE) { + + fprintf (state->ofp, " notq %%%s\n", lo); + fprintf (state->ofp, " notq %%%s\n", hi); + + } else if (unary_op == TOK_XMARK) { + + fprintf (state->ofp, " orq %%%s, %%%s\n", hi, lo); + fprintf (state->ofp, " setz %%al\n"); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (lo, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%s\n", lo); + } + + fprintf (state->ofp, " xorq %%%s, %%%s\n", hi, hi); + + } + + } + + } + + return; + + } + + if (emit_load_prefix_incdec_to_pair_now (lo, hi)) { + return; + } + + if (tok.kind == TOK_AMPER) { + + /* + * Address-of is a valid pointer-valued operand in 64-bit contexts. + * + * The 32-bit RHS path already handles &object, &(postfix), + * &object.member and &object[index]. The pair path used for 64-bit + * / pointer-sized expressions did not, so expressions such as: + * + * (char *)&format + n + * + * fell through to const64_from_current_operand(). That helper delegates + * to expr_const64(), which quite rightly rejects the identifier after + * '&' as not being an integer constant expression. + * + * Reuse the normal scalar address-of parser, then widen the pointer + * value into the requested pair result. This keeps the fix generic: + * it is not tied to va_start, sizeof, or any particular macro. + */ + emit_load_assignment_rhs_to_reg (lo); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + } else { + fprintf (state->ofp, " xorq %%%s, %%%s\n", hi, hi); + } + + } + + set_rhs_last_pointer_info (1, DATA_INT & 0x1f); + 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_LPAREN) { + + const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + int cast_deref_size = DATA_INT & 0x1f; + + get_token (); + + if (is_type_start (tok.kind) && parse_deref_cast_type_name (&cast_deref_size)) { + + emit_load_assignment_rhs_to_reg (lo); + + if ((cast_deref_size & 0x1f) == (DATA_LLONG & 0x1f)) { + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + + emit_load_deref_reg_now (lo, cast_deref_size); + emit_extend_pair_high_from_low (lo, hi, cast_deref_size, 1); + + } + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (lo); + expect (TOK_RPAREN, ")"); + + emit_load_deref_reg_now (lo, DATA_INT & 0x1f); + emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1); + + return; + + } + + 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 (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!src && global_index >= 0 && tok.kind == TOK_LPAREN && + get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) { + + const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + int fptr_depth = get_global_symbol_pointer_depth (name); + int fpointed_size = get_global_symbol_pointed_size (name); + + if (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, lo, name_start, name_caret, name_line); + + if (fptr_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (fptr_depth == 1 && fpointed_size > 0) { + deref_size = fpointed_size & 0x1f; + } + + if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + + emit_load_deref_reg_now (lo, deref_size); + emit_extend_pair_high_from_low (lo, hi, deref_size, 1); + + } + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + 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) { + + const char *dhi = amd64_dword_reg_name_from_any (hi); + const char *qhi = amd64_qword_reg_name_from_any (hi); + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov %s, dword [%s + 4]\n", dhi, qhi); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", dhi, qhi); + } + + } else { + fprintf (state->ofp, " movl 4(%%%s), %%%s\n", amd64_qword_reg_name_from_any (hi), amd64_dword_reg_name_from_any (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 (); + + emit_load_const64_to_pair_now (lo, hi, v); + return; + + } + + if (tok.kind == TOK_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + + get_token (); + emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + + if (va_size == (DATA_LLONG & 0x1f) && va_pointer == 0) { + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + } else { + + emit_copy_reg_now (lo, addr_reg); + emit_load_deref_reg_now (lo, va_size); + + emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); + + } + + 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi"; + emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + + if (va_size == (DATA_LLONG & 0x1f) && !va_floating && va_pointer == 0) { + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + } else { + + emit_copy_reg_now (lo, addr_reg); + emit_load_deref_reg_now (lo, va_size); + emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); + + } + + free (name); + return; + + } + + 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, "rax", name_start, name_caret, name_line); + + if (get_global_function_returns_floating (name)) { + + if (state->ofp) { + + if (get_global_symbol_size (name) == (DATA_FLOAT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + /* + * Keep the temporary XMM0 spill slot 8 bytes wide. + * Using a 4-byte stack adjustment leaves RSP misaligned + * while copying a float return value into the integer + * return pair used by the rest of this backend. + */ + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " movss dword [rsp], xmm0\n" : " movss dword ptr [rsp], xmm0\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp]\n" : " mov %s, dword ptr [rsp]\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " xor %s, %s\n", amd64_dword_reg_name_from_any (hi), amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + /* Keep the temporary XMM0 spill slot 8 bytes wide and preserve stack alignment. */ + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movss %%xmm0, (%%rsp)\n"); + fprintf (state->ofp, " movl (%%rsp), %%%s\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " xorl %%%s, %%%s\n", amd64_dword_reg_name_from_any (hi), amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + } else if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " movsd qword [rsp], xmm0\n" : " movsd qword ptr [rsp], xmm0\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp]\n" : " mov %s, dword ptr [rsp]\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp + 4]\n" : " mov %s, dword ptr [rsp + 4]\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movsd %%xmm0, (%%rsp)\n"); + fprintf (state->ofp, " movl (%%rsp), %%%s\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " movl 4(%%rsp), %%%s\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + } + + free (name); + return; + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (strcmp (lo, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", lo); + } + + if (strcmp (hi, "rdx") != 0) { + fprintf (state->ofp, " mov %s, rdx\n", hi); + } + + } else { + + if (strcmp (lo, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%s\n", lo); + } + + if (strcmp (hi, "rdx") != 0) { + fprintf (state->ofp, " movq %%rdx, %%%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 = src->tag_name; + + } 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, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rdx"; + int lvalue_size = index_step_size (source_size); + + get_token (); + + if (assign_op == TOK_ASSIGN && (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_now ())) { + + emit_push_reg_now (lo); + emit_load_floating_rhs_expression_now (lvalue_size); + + emit_pop_reg_now (addr_reg); + emit_store_floating_member_to_addr_reg_now (addr_reg, 0, lvalue_size); + + free (name); + return; + } + + 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_ex_now (lo, hi, source_size, src ? (src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned) : (get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name))); + + 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_ex (lo, src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex (lo, src->offset, src->size, src->is_unsigned); + } + + 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 rax:rdx (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, " xorq %%%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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (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, " xorq %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " xorq %%%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, " xorq %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " xorq %%%s, %%%s\n", hi, hi); + + } + + } + + return; + + } + + { + + int64_s v = const64_from_current_operand (); + emit_load_const64_to_pair_now (lo, hi, v); + + } + +} + +static void emit_assignment_divmod64 (int is_unsigned, int want_mod) { + + if (!state->ofp) { + return; + } + + /* + * AMD64 has native 64-bit division. Do not emit the old i386 + * software 64/64 divide helper here. The 64-bit binary-expression + * path leaves the dividend in RAX and the divisor in RBX. DIV/IDIV + * produces quotient in RAX and remainder in RDX. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (is_unsigned) { + + fprintf (state->ofp, " xor rdx, rdx\n"); + fprintf (state->ofp, " div rbx\n"); + + } else { + + fprintf (state->ofp, " cqo\n"); + fprintf (state->ofp, " idiv rbx\n"); + + } + + if (want_mod) { + fprintf (state->ofp, " mov rax, rdx\n"); + } + + } else { + + if (is_unsigned) { + + fprintf (state->ofp, " xorq %%rdx, %%rdx\n"); + fprintf (state->ofp, " divq %%rbx\n"); + + } else { + + fprintf (state->ofp, " cqto\n"); + fprintf (state->ofp, " idivq %%rbx\n"); + + } + + if (want_mod) { + fprintf (state->ofp, " movq %%rdx, %%rax\n"); + } + + } + +} + +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 rax, rbx\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " sub rax, rbx\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imul rax, rbx\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 rax, rbx\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " or rax, rbx\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xor rax, rbx\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " mov rcx, rbx\n"); + fprintf (state->ofp, " cmp rcx, 64\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l1); + fprintf (state->ofp, " shl rax, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1); + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " mov rcx, rbx\n"); + fprintf (state->ofp, " cmp rcx, 64\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l1); + fprintf (state->ofp, is_unsigned ? " shr rax, cl\n" : " sar rax, cl\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1); + fprintf (state->ofp, is_unsigned ? " xor rax, rax\n" : " sar rax, 63\n"); + fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2); + + break; + + default: + + break; + + } + + } else { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " addq %%rbx, %%rax\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " subq %%rbx, %%rax\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imulq %%rbx, %%rax\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, " andq %%rbx, %%rax\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " orq %%rbx, %%rax\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xorq %%rbx, %%rax\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " movq %%rbx, %%rcx\n"); + fprintf (state->ofp, " cmpq $64, %%rcx\n"); + fprintf (state->ofp, " jae .L%d\n", l1); + fprintf (state->ofp, " shlq %%cl, %%rax\n"); + fprintf (state->ofp, " jmp .L%d\n", l2); + fprintf (state->ofp, ".L%d:\n", l1); + fprintf (state->ofp, " xorq %%rax, %%rax\n"); + fprintf (state->ofp, ".L%d:\n", l2); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + l1 = anon_label++; + l2 = anon_label++; + + fprintf (state->ofp, " movq %%rbx, %%rcx\n"); + fprintf (state->ofp, " cmpq $64, %%rcx\n"); + fprintf (state->ofp, " jae .L%d\n", l1); + fprintf (state->ofp, is_unsigned ? " shrq %%cl, %%rax\n" : " sarq %%cl, %%rax\n"); + fprintf (state->ofp, " jmp .L%d\n", l2); + fprintf (state->ofp, ".L%d:\n", l1); + fprintf (state->ofp, is_unsigned ? " xorq %%rax, %%rax\n" : " sarq $63, %%rax\n"); + fprintf (state->ofp, ".L%d:\n", l2); + + break; + + default: + + break; + + } + + } + +} + +static void amd64_emit_load_xmm0_to_floating_stack_now (int size) { + + if (!state->ofp) { + return; + } + + if (size == (DATA_FLOAT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movss dword [rsp], xmm0\n" : " movss dword ptr [rsp], xmm0\n")); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld dword [rsp]\n" : " fld dword ptr [rsp]\n")); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movss %%xmm0, (%%rsp)\n"); + fprintf (state->ofp, " flds (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsd qword [rsp], xmm0\n" : " movsd qword ptr [rsp], xmm0\n")); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld qword [rsp]\n" : " fld qword ptr [rsp]\n")); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movsd %%xmm0, (%%rsp)\n"); + fprintf (state->ofp, " fldl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + +} + +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_rbp_offset (memref, sizeof (memref), offset); + fprintf (state->ofp, " lea %s, %s\n", reg, memref); + + } else { + fprintf (state->ofp, " leaq %ld(%%rbp), %%%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; + + postfix_member_calling_convention = TOK_EOF; + + { + + 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; + postfix_member_calling_convention = last_found_member_calling_convention; + + 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, " addq $%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 ("rdx"); + 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 ("rdx"); + + 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 ("rdx"); + emit_pop_reg_now (reg); + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", 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, " addq $%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, " addq $%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, " addq $%d, %%%s\n", offset, reg); + } + + } + + continue; + + } + + if (state->ofp) { + + if (strcmp (reg, "rdx") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rdx, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rdx\n", reg); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *dreg = amd64_dword_reg_name_from_any (reg); + int qword = (size == (DATA_PTR & 0x1f) || size == (DATA_LLONG & 0x1f)); + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == 1) { + fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", dreg, reg, offset); + } else if (size == 2) { + fprintf (state->ofp, " movzx %s, word [%s + %d]\n", dreg, reg, offset); + } else { + + if (qword) { + fprintf (state->ofp, " mov %s, qword [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " movsxd %s, dword [%s + %d]\n", reg, reg, offset); + } + + } + + } else { + + if (size == 1) { + fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", dreg, reg, offset); + } else if (size == 2) { + fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", dreg, reg, offset); + } else { + + if (qword) { + fprintf (state->ofp, " mov %s, qword ptr [%s + %d]\n", reg, reg, offset); + } else { + fprintf (state->ofp, " movsxd %s, dword ptr [%s + %d]\n", reg, reg, offset); + } + + } + + } + + } else { + + if (size == 1) { + fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, reg, amd64_dword_reg_name_from_any (reg)); + } else if (size == 2) { + fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, amd64_dword_reg_name_from_any (reg)); + } else if (size == (DATA_PTR & 0x1f) || size == (DATA_LLONG & 0x1f)) { + fprintf (state->ofp, " movq %d(%%%s), %%%s\n", offset, reg, reg); + } else { + fprintf (state->ofp, " movslq %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 qword [rdx + %d], %d\n", arith, postfix_member_offset, step); + } else { + fprintf (state->ofp, " %s qword ptr [rdx + %d], %d\n", arith, postfix_member_offset, step); + } + + return; + + } else { + + fprintf (state->ofp, " %sq $%d, %d(%%rdx)\n", arith, step, postfix_member_offset); + return; + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + const char *opsize = "dword"; + int qword = (postfix_member_size == (DATA_LLONG & 0x1f) || postfix_member_size == (DATA_PTR & 0x1f)); + + if (postfix_member_size == 1) { + opsize = "byte"; + } else if (postfix_member_size == 2) { + opsize = "word"; + } else if (qword) { + opsize = "qword"; + } + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s [rdx + %d]\n", insn, opsize, postfix_member_offset); + } else { + fprintf (state->ofp, " %s %s ptr [rdx + %d]\n", insn, opsize, postfix_member_offset); + } + + } else { + + if (postfix_member_size == 1) { + fprintf (state->ofp, " %sb %d(%%rdx)\n", insn, postfix_member_offset); + } else if (postfix_member_size == 2) { + fprintf (state->ofp, " %sw %d(%%rdx)\n", insn, postfix_member_offset); + } else if (postfix_member_size == (DATA_LLONG & 0x1f) || postfix_member_size == (DATA_PTR & 0x1f)) { + fprintf (state->ofp, " %sq %d(%%rdx)\n", insn, postfix_member_offset); + } else { + fprintf (state->ofp, " %sl %d(%%rdx)\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, "rax", rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("rdx"); + 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 rhs_current_operand_is_floating_now (void); + +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 () || rhs_current_operand_is_floating_now ()) { + + emit_push_reg_now ("rdx"); + emit_load_floating_rhs_expression_now (assign_member_size); + + emit_pop_reg_now ("rdx"); + emit_store_floating_member_to_addr_reg_now ("rdx", 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 ("rdx"); + + 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 ("rdx"); + free (rhs_name); + + return 1; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", assign_member_offset, assign_member_size)) { + return 1; + } + + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("rdx"); + + } else { + + emit_push_reg_now ("rdx"); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_member_to_addr_reg_now ("rdx", 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_ex_now (const char *reg, int size, int is_unsigned) { + + const char *op8; + const char *op16; + const char *qreg; + const char *dreg; + const char *att_dst_reg; + + if (!state->ofp) { + return; + } + + op8 = is_unsigned ? "movzx" : "movsx"; + op16 = is_unsigned ? "movzx" : "movsx"; + + qreg = amd64_qword_reg_name_from_any (reg); + dreg = amd64_dword_reg_name_from_any (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s %s, byte [%s]\n", op8, is_unsigned ? dreg : qreg, qreg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %s, word [%s]\n", op16, is_unsigned ? dreg : qreg, qreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov %s, qword [%s]\n", qreg, qreg); + } else if (is_unsigned) { + fprintf (state->ofp, " mov %s, dword [%s]\n", dreg, qreg); + } else { + fprintf (state->ofp, " movsxd %s, dword [%s]\n", qreg, qreg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s %s, byte ptr [%s]\n", op8, is_unsigned ? dreg : qreg, qreg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %s, word ptr [%s]\n", op16, is_unsigned ? dreg : qreg, qreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov %s, qword ptr [%s]\n", qreg, qreg); + } else if (is_unsigned) { + fprintf (state->ofp, " mov %s, dword ptr [%s]\n", dreg, qreg); + } else { + fprintf (state->ofp, " movsxd %s, dword ptr [%s]\n", qreg, qreg); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + + att_dst_reg = is_unsigned ? dreg : qreg; + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbq", qreg, att_dst_reg); + + } else if (size == (DATA_SHORT & 0x1f)) { + + att_dst_reg = is_unsigned ? dreg : qreg; + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswq", qreg, att_dst_reg); + + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq (%%%s), %%%s\n", qreg, qreg); + } else if (is_unsigned) { + fprintf (state->ofp, " movl (%%%s), %%%s\n", qreg, dreg); + } else { + fprintf (state->ofp, " movslq (%%%s), %%%s\n", qreg, qreg); + } + + } + +} + +static void emit_load_deref_reg_now (const char *reg, int size) { + emit_load_deref_reg_ex_now (reg, size, 0); +} + +static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size) { + + const char *breg; + const char *wreg; + const char *dreg; + const char *qreg; + + if (suppress_next_struct_return_scalar_store) { + + suppress_next_struct_return_scalar_store = 0; + return; + + } + + if (!state->ofp) { + return; + } + + breg = amd64_byte_reg_name_from_any (value_reg); + wreg = amd64_word_reg_name_from_any (value_reg); + dreg = amd64_dword_reg_name_from_any (value_reg); + qreg = amd64_qword_reg_name_from_any (value_reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " mov byte [%s], %s\n", addr_reg, breg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " mov word [%s], %s\n", addr_reg, wreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov qword [%s], %s\n", addr_reg, qreg); + } else { + fprintf (state->ofp, " mov dword [%s], %s\n", addr_reg, dreg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " mov byte ptr [%s], %s\n", addr_reg, breg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " mov word ptr [%s], %s\n", addr_reg, wreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov qword ptr [%s], %s\n", addr_reg, qreg); + } else { + fprintf (state->ofp, " mov dword ptr [%s], %s\n", addr_reg, dreg); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%s, (%%%s)\n", breg, addr_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%s, (%%%s)\n", wreg, addr_reg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq %%%s, (%%%s)\n", qreg, addr_reg); + } else { + fprintf (state->ofp, " movl %%%s, (%%%s)\n", dreg, addr_reg); + } + + } + +} + +static int emit_handle_subscript_after_loaded_pointer_to_reg_now (const char *reg, int pointer_depth, int pointed_size, int pointed_is_unsigned) { + + 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 ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + + emit_store_reg_to_deref_reg_now ("rdx", reg, subscript_elem_size); + clear_rhs_last_pointer_info (); + + return 1; + + } + + emit_load_deref_reg_ex_now (reg, subscript_elem_size, pointer_depth <= 1 ? pointed_is_unsigned : 0); + + 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) { + + const char *qreg; + + if (!state->ofp) { + return; + } + + qreg = amd64_qword_reg_name_from_any (dst_reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsx %s, byte [%s + %d]\n", qreg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %s, word [%s + %d]\n", qreg, addr_reg, offset); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov %s, qword [%s + %d]\n", qreg, addr_reg, offset); + } else { + fprintf (state->ofp, " movsxd %s, dword [%s + %d]\n", qreg, addr_reg, offset); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsx %s, byte ptr [%s + %d]\n", qreg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %s, word ptr [%s + %d]\n", qreg, addr_reg, offset); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " mov %s, qword ptr [%s + %d]\n", qreg, addr_reg, offset); + } else { + fprintf (state->ofp, " movsxd %s, dword ptr [%s + %d]\n", qreg, addr_reg, offset); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsbq %d(%%%s), %%%s\n", offset, addr_reg, qreg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movswq %d(%%%s), %%%s\n", offset, addr_reg, qreg); + } else if (amd64_scalar_size_is_qword (size)) { + fprintf (state->ofp, " movq %d(%%%s), %%%s\n", offset, addr_reg, qreg); + } else { + fprintf (state->ofp, " movslq %d(%%%s), %%%s\n", offset, addr_reg, qreg); + } + + } + +} + +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, " addq $%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, " addq $%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, " addq $%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, " addq $%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 [rax + %d]\n" : " mov ecx, dword ptr [rax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [rdx + %d], ecx\n" : " mov dword ptr [rdx + %d], ecx\n", offset); + + } else { + + fprintf (state->ofp, " movl %d(%%rax), %%ecx\n", offset); + fprintf (state->ofp, " movl %%ecx, %d(%%rdx)\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 [rax + %d]\n" : " mov cx, word ptr [rax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [rdx + %d], cx\n" : " mov word ptr [rdx + %d], cx\n", offset); + + } else { + + fprintf (state->ofp, " movw %d(%%rax), %%cx\n", offset); + fprintf (state->ofp, " movw %%cx, %d(%%rdx)\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 [rax + %d]\n" : " mov cl, byte ptr [rax + %d]\n", offset); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov byte [rdx + %d], cl\n" : " mov byte ptr [rdx + %d], cl\n", offset); + + } else { + + fprintf (state->ofp, " movb %d(%%rax), %%cl\n", offset); + fprintf (state->ofp, " movb %%cl, %d(%%rdx)\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 ("rax", src, src_name); + emit_load_symbol_address_for_copy_now ("rdx", 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, "rax", call_start, call_caret, call_line); + emit_pop_reg_now ("rdx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\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 ("rax", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rax", 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 ("rcx", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", ptr_name, DATA_PTR); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? + " mov rax, qword [rcx + %d]\n" : + " mov rax, qword ptr [rcx + %d]\n", member_offset); + + if (member_incdec) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? + " %s qword [rcx + %d], %d\n" : + " %s qword ptr [rcx + %d], %d\n", + member_incdec_op == TOK_INCR ? "add" : "sub", + member_offset, member_elem_size); + + } + + } else { + + fprintf (state->ofp, " movq %d(%%rcx), %%rax\n", member_offset); + + if (member_incdec) { + + fprintf (state->ofp, " %sq $%d, %d(%%rcx)\n", + member_incdec_op == TOK_INCR ? "add" : "sub", + member_elem_size, member_offset); + + } + + } + + } + + emit_pop_reg_now ("rdx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\n", offset); + } + + } + + emit_copy_fixed_size_now (size); + + free (member); + free (ptr_name); + + return 1; + + } + + emit_pop_reg_now ("rdx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\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 ("rax", ptr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", ptr_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rax", ptr_name, DATA_PTR); + } + + emit_load_deref_reg_now ("rax", 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 ("rax", 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 ("rax", DATA_PTR); + } + + } else { + have_direct_object_pointer = 0; + } + + if (member_offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rax, %d\n", member_offset); + } else { + fprintf (state->ofp, " addq $%d, %%rax\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 ("rdx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\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 ("rax", 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 ("rax", rhs_sym, rhs_name); + } + + emit_pop_reg_now ("rdx"); + + if (offset != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\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, " addq $%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 = "rax"; + } + + 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); + + if (src->is_array && !src->pointer_depth) { + current_object_tag_name = src->tag_name; + } else if (!src->is_array && src->pointer_depth > 0) { + current_object_tag_name = src->pointed_tag_name; + } + + } 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 (get_global_symbol_array (src_name) && !get_global_symbol_pointer_depth (src_name)) { + current_object_tag_name = get_global_symbol_tag_name (src_name); + } else if (!get_global_symbol_array (src_name) && get_global_symbol_pointer_depth (src_name) > 0) { + current_object_tag_name = get_global_symbol_tag_name (src_name); + } + + } + + if (elem_size <= 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, " addq $%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_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_add_const_to_reg_now (const char *reg, int offset) { + + if (!state->ofp || !reg || offset == 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add %s, %d\n", reg, offset); + } else { + fprintf (state->ofp, " addq $%d, %%%s\n", offset, 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 void save_parse_type_state_now ( + + int *saved_type_size, + int *saved_storage_class, + int *saved_is_aggregate, + int *saved_is_void, + int *saved_is_unsigned, + int *saved_is_floating, + int *saved_has_tag, + int *saved_is_inline, + int *saved_field_count, + int saved_fields[MAX_AGG_FIELDS], + int *saved_declarator_is_pointer, + int *saved_declarator_pointer_depth, + int *saved_declarator_has_array, + int *saved_declarator_has_function, + int *saved_declarator_array_unsized, + int *saved_declarator_array_dimensions, + long *saved_declarator_array_count, + long *saved_declarator_first_array_count) { + + int i; + + *saved_type_size = parsed_type_size; + *saved_storage_class = parsed_storage_class; + *saved_is_aggregate = parsed_type_is_aggregate; + *saved_is_void = parsed_type_is_void; + *saved_is_unsigned = parsed_type_is_unsigned; + *saved_is_floating = parsed_type_is_floating; + *saved_has_tag = parsed_type_has_tag; + *saved_is_inline = parsed_type_is_inline; + *saved_field_count = parsed_field_count; + + for (i = 0; i < *saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + *saved_declarator_is_pointer = declarator_is_pointer; + *saved_declarator_pointer_depth = declarator_pointer_depth; + *saved_declarator_has_array = declarator_has_array; + *saved_declarator_has_function = declarator_has_function; + *saved_declarator_array_unsized = declarator_array_unsized; + *saved_declarator_array_dimensions = declarator_array_dimensions; + *saved_declarator_array_count = declarator_array_count; + *saved_declarator_first_array_count = declarator_first_array_count; + +} + +static void restore_parse_type_state_now ( + int saved_type_size, + int saved_storage_class, + int saved_is_aggregate, + int saved_is_void, + int saved_is_unsigned, + int saved_is_floating, + int saved_has_tag, + int saved_is_inline, + int saved_field_count, + int saved_fields[MAX_AGG_FIELDS], + int saved_declarator_is_pointer, + int saved_declarator_pointer_depth, + int saved_declarator_has_array, + int saved_declarator_has_function, + int saved_declarator_array_unsized, + int saved_declarator_array_dimensions, + long saved_declarator_array_count, + long saved_declarator_first_array_count) { + + int i; + + 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_dimensions = saved_declarator_array_dimensions; + declarator_array_count = saved_declarator_array_count; + declarator_first_array_count = saved_declarator_first_array_count; + +} + +static int parse_builtin_va_arg_type_now (int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { + + int saved_type_size; + int saved_storage_class; + int saved_is_aggregate; + int saved_is_void; + int saved_is_unsigned; + int saved_is_floating; + int saved_has_tag; + int saved_is_inline; + int saved_field_count; + int saved_fields[MAX_AGG_FIELDS]; + int saved_declarator_is_pointer; + int saved_declarator_pointer_depth; + int saved_declarator_has_array; + int saved_declarator_has_function; + int saved_declarator_array_unsized; + int saved_declarator_array_dimensions; + + long saved_declarator_array_count; + long saved_declarator_first_array_count; + + char *type_name = 0; + + int base_size; + int pointer_depth; + int is_unsigned; + int is_floating; + + save_parse_type_state_now (&saved_type_size, &saved_storage_class, + &saved_is_aggregate, &saved_is_void, &saved_is_unsigned, + &saved_is_floating, &saved_has_tag, &saved_is_inline, + &saved_field_count, saved_fields, &saved_declarator_is_pointer, + &saved_declarator_pointer_depth, &saved_declarator_has_array, + &saved_declarator_has_function, &saved_declarator_array_unsized, + &saved_declarator_array_dimensions, &saved_declarator_array_count, + &saved_declarator_first_array_count); + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; + declarator_first_array_count = 1; + declarator_array_dimensions = 0; + + parse_type_spec (); + base_size = parsed_type_size & 0x1f; + + is_unsigned = parsed_type_is_unsigned; + is_floating = parsed_type_is_floating; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&type_name); + } + + pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0; + + if (type_name) { + free (type_name); + } + + if (out_pointer) { + *out_pointer = pointer_depth; + } + + if (out_unsigned) { + *out_unsigned = is_unsigned; + } + + if (out_floating) { + *out_floating = is_floating; + } + + if (out_size) { + *out_size = pointer_depth > 0 ? (DATA_PTR & 0x1f) : base_size; + } + + restore_parse_type_state_now (saved_type_size, saved_storage_class, + saved_is_aggregate, saved_is_void, saved_is_unsigned, + saved_is_floating, saved_has_tag, saved_is_inline, + saved_field_count, saved_fields, saved_declarator_is_pointer, + saved_declarator_pointer_depth, saved_declarator_has_array, + saved_declarator_has_function, saved_declarator_array_unsized, + saved_declarator_array_dimensions, saved_declarator_array_count, + saved_declarator_first_array_count); + + return 1; + +} + +static int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { + + char *name; + + const char *name_start; + const char *name_caret; + const char *scratch_reg; + + unsigned long name_line; + unsigned long inc; + + struct local_symbol *src; + + int global_index; + int size = DATA_INT & 0x1f; + int is_unsigned = 1; + int pointer_depth = 0; + int is_floating = 0; + int deref_lvalue = 0; + int paren_lvalue = 0; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + paren_lvalue = 1; + get_token (); + + } + + if (tok.kind == TOK_STAR) { + + deref_lvalue = 1; + 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 va_list object in __scc_builtin_va_arg"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + if (paren_lvalue) { + expect (TOK_RPAREN, ")"); + } + + expect (TOK_COMMA, ","); + + if (!is_type_start (tok.kind)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type name in __scc_builtin_va_arg"); + + free (name); + return 1; + + } + + parse_builtin_va_arg_type_now (&size, &is_unsigned, &pointer_depth, &is_floating); + expect (TOK_RPAREN, ")"); + + if (size <= 0) { + size = DATA_INT & 0x1f; + } + + /* + * AMD64 varargs are slot based. Even small integer types occupy an + * 8-byte argument slot in the register home/overflow area, so advancing + * by the C object size makes the next va_arg read from the middle of the + * previous slot. + */ + inc = (unsigned long) align_up_long ((size > 0 ? size : (DATA_INT & 0x1f)), DATA_PTR & 0x1f); + + if (inc < (unsigned long) (DATA_PTR & 0x1f)) { + inc = DATA_PTR & 0x1f; + } + + 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) { + + scratch_reg = strcmp (reg, "rdx") == 0 ? "rcx" : "rdx"; + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (scratch_reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (scratch_reg, src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (scratch_reg, name, DATA_PTR); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [%s]\n" : " mov %s, qword ptr [%s]\n", reg, scratch_reg); + fprintf (state->ofp, " push %s\n", reg); + fprintf (state->ofp, " add %s, %lu\n", reg, inc); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov qword [%s], %s\n" : " mov qword ptr [%s], %s\n", scratch_reg, reg); + fprintf (state->ofp, " pop %s\n", reg); + + } else { + + fprintf (state->ofp, " movq (%%%s), %%%s\n", scratch_reg, reg); + fprintf (state->ofp, " pushq %%%s\n", reg); + fprintf (state->ofp, " addq $%lu, %%%s\n", inc, reg); + fprintf (state->ofp, " movq %%%s, (%%%s)\n", reg, scratch_reg); + fprintf (state->ofp, " subq $%lu, %%%s\n", inc, reg); + fprintf (state->ofp, " popq %%%s\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, " push %s\n", reg); + fprintf (state->ofp, " add %s, %lu\n", reg, inc); + + } else { + + fprintf (state->ofp, " pushq %%%s\n", reg); + fprintf (state->ofp, " addq $%lu, %%%s\n", inc, 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 (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " pop %s\n", reg); + } else { + fprintf (state->ofp, " popq %%%s\n", reg); + } + + } + + } + + if (out_size) { + *out_size = size; + } + + if (out_unsigned) { + *out_unsigned = is_unsigned; + } + + if (out_pointer) { + *out_pointer = pointer_depth; + } + + if (out_floating) { + *out_floating = is_floating; + } + + 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, " addq $%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 addq $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 = "rax"; + } + + addr_reg = (strcmp (reg, "rdx") == 0) ? "rcx" : "rdx"; + + 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 ? " addq $%d, %%%s\n" : " subq $%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 = "rax"; + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("rcx", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now (reg, "rcx", 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 ? " addq $%d, %%%s\n" : " subq $%d, %%%s\n", step, reg); + } + + } + + emit_store_reg_to_deref_reg_now ("rcx", 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 = "rax"; + } + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg ("rcx", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", name, DATA_PTR); + } + + emit_load_member_from_addr_reg_now ("rdx", "rcx", 0, DATA_PTR & 0x1f); + emit_push_reg_now ("rdx"); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add rdx, %d\n" : " sub rdx, %d\n", step); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addq $%d, %%rdx\n" : " subq $%d, %%rdx\n", step); + } + + } + + emit_store_reg_to_deref_reg_now ("rcx", "rdx", DATA_PTR & 0x1f); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", reg, store_size); + + free (name); + return 1; + +} + +static void emit_scale_reg_by_const_now (const char *reg, int scale); + +static int source_starts_deref_assignment_at_now (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 == '=' && p[1] != '=') { + return 1; + } + + if ((p[0] == '+' || p[0] == '-' || p[0] == '*' || p[0] == '/' || + p[0] == '%' || p[0] == '&' || p[0] == '^' || p[0] == '|') && + p[1] == '=') { + return 1; + } + + if ((p[0] == '<' && p[1] == '<' && p[2] == '=') || + (p[0] == '>' && p[1] == '>' && p[2] == '=')) { + return 1; + } + + return 0; + +} + +static int emit_load_deref_assignment_expression_to_reg_now (const char *reg) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + + struct local_symbol *sym; + int global_index; + + enum token_kind op; + + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int value_pointer_depth = 0; + int store_size = DATA_INT & 0x1f; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (!source_starts_deref_assignment_at_now (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 (!is_assignment_operator (tok.kind)) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + pointer_depth = sym->pointer_depth; + pointed_size = sym->pointed_size; + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("rcx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", sym->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 ("rcx", 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; + + } + + value_pointer_depth = pointer_depth - 1; + + if (value_pointer_depth > 0) { + store_size = DATA_PTR & 0x1f; + } else if (pointed_size > 0) { + store_size = pointed_size & 0x1f; + } + + if (store_size <= 0) { + store_size = DATA_INT & 0x1f; + } + + emit_push_reg_now ("rcx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_load_deref_reg_now ("rcx", store_size); + + emit_push_reg_now ("rcx"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + emit_pop_reg_now ("rax"); + + if ((op == TOK_PLUSEQ || op == TOK_MINUSEQ) && value_pointer_depth > 0 && + index_step_size (pointed_size) > 1) { + emit_scale_reg_by_const_now ("rdx", index_step_size (pointed_size)); + } + + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("rcx"); + emit_store_reg_to_deref_reg_now ("rcx", "rax", store_size); + + if (reg && strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%s\n", reg); + } + + } + + expect (TOK_RPAREN, ")"); + + if (value_pointer_depth > 0) { + set_rhs_last_pointer_info (value_pointer_depth, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return 1; + +} + +static int source_starts_double_deref_at_now (const char *p) { + + if (!p || *p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '*'; + +} + +static int current_floating_token_is_nonzero_now (void) { + + if (tok.kind == TOK_CFLOAT) { + + union { float f; unsigned long u; } v; + + v.u = 0; + v.f = tok.val.f; + + return (v.u & 0x7fffffffUL) != 0; + + } + + { + + union { double d; unsigned long u[2]; } v; + + v.u[0] = 0; + v.u[1] = 0; + + if (tok.kind == TOK_CLDOUBLE) { + v.d = tok.val.ld; + } else { + v.d = tok.val.d; + } + + return ((v.u[1] & 0x7fffffffUL) != 0 || v.u[0] != 0); + + } + +} + +static void emit_load_assignment_rhs_to_reg (const char *reg) { + + clear_rhs_last_pointer_info (); + + if (tok.kind == TOK_STAR && source_starts_double_deref_at_now (tok.caret)) { + + int deref_size; + int inner_pointer_depth; + int inner_pointed_size; + + get_token (); + emit_load_assignment_rhs_to_reg (reg); + + inner_pointer_depth = rhs_last_pointer_depth; + inner_pointed_size = rhs_last_pointed_size; + + if (inner_pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (inner_pointer_depth == 1 && inner_pointed_size > 0) { + deref_size = inner_pointed_size & 0x1f; + } else { + deref_size = DATA_INT & 0x1f; + } + + emit_load_deref_reg_now (reg, deref_size); + + if (inner_pointer_depth > 1) { + set_rhs_last_pointer_info (inner_pointer_depth - 1, inner_pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + 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 (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 grerdily 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; + + if (emit_load_deref_assignment_expression_to_reg_now (reg)) { + return; + } + + /* + * 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: + * + * movq off(%rbp), %rax + * movq (%rax), %rax + * + * 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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_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; + declarator_first_array_count = 1; + + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + 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 (tok.kind != TOK_LPAREN && current_integer_expr_is_foldable_now ()) { + + 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 ("rdx"); + 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 ("rdx"); + 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 ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + + emit_store_reg_to_deref_reg_now ("rdx", 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 () || rhs_current_operand_is_floating_now ()) { + + emit_push_reg_now ("rdx"); + emit_load_floating_rhs_expression_now (assign_member_size); + + emit_pop_reg_now ("rdx"); + emit_store_floating_member_to_addr_reg_now ("rdx", 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 ("rdx"); + + 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 ("rdx"); + free (rhs_name); + + return; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", assign_member_offset, assign_member_size)) { + return; + } + + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("rdx"); + + } else { + + /* + * emit_apply_postfix_member_access_to_reg_now() has already + * loaded the member value into reg and left the containing + * object address in rdx. 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 ("rdx"); + emit_push_reg_now (reg); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_member_to_addr_reg_now ("rdx", 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 ? src->pointed_tag_name : src->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, " addq $%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->is_array ? (src->tag_name ? src->tag_name : src->pointed_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, " addq $%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; + int member_load_size = DATA_PTR & 0x1f; + + 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 > 0) { + member_load_size = DATA_PTR & 0x1f; + } else { + member_load_size = member_size; + } + + 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 ("rdx", lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs_sym->offset, DATA_PTR); + } + + } else if (find_global_symbol (lhs_name) >= 0) { + emit_load_global_to_reg ("rdx", 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 && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rdx, %d\n", offset); + } else { + fprintf (state->ofp, " addq $%d, %%rdx\n", offset); + } + + } + + emit_push_reg_now ("rdx"); + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", reg, member_load_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 (member_size > (DATA_PTR & 0x1f) && member_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, " addq $%d, %%%s\n", offset, reg); + } + + } + + } else { + emit_load_member_from_addr_reg_now (reg, reg, offset, member_load_size); + } + + if (lhs_has_postfix) { + /* Already applied above only for assignment forms. */ + } + + if (member_pointer_depth > 0) { + set_rhs_last_pointer_info (member_pointer_depth, member_elem_size); + } else { + clear_rhs_last_pointer_info (); + } + + 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 ("rdx", lhs_sym->static_label); + } else { + emit_load_local_address_to_reg_now ("rdx", lhs_sym->offset); + } + + } else if (lhs_sym->is_static && lhs_sym->static_label) { + emit_load_global_to_reg ("rdx", lhs_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", 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 ("rdx", lhs_name); + } else { + emit_load_global_to_reg ("rdx", 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 ("rdx"); + + 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 ("rdx"); + + if (!emit_store_assignment_to_aggregate_address_now ("rdx", 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 ("rax", "rdx", lhs_deref_is_unsigned); + emit_pop_reg_now ("rcx"); + + emit_store_pair_to_deref_reg_now ("rcx", "rax", "rdx"); + + } else { + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("rdx"); + + emit_store_reg_to_deref_reg_now ("rdx", 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, lhs_sym->pointed_is_unsigned)) { + + 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), get_global_symbol_pointed_is_unsigned (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)) { + + /* + * 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 ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", reg, deref_size); + + return; + + } + + /* + * parse_deref_cast_type_name() consumes only the cast type + * parentheses. The operand being cast is still the current + * token, e.g. the ptr in: + * + * *(size_t *)ptr + sizeof(size_t) + * + * Load that address expression before applying the outer + * unary '*'. Otherwise the caller sees the unconsumed + * identifier/operator and reports a false "expected )". + */ + emit_load_assignment_rhs_to_reg (reg); + 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 (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + emit_push_reg_now (reg); + get_token (); + + if (assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + emit_load_deref_reg_now (reg, deref_size); + + emit_push_reg_now (reg); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + emit_pop_reg_now (reg); + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", 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) { + + int member_qword = (member_size == (DATA_PTR & 0x1f) || member_size == (DATA_LLONG & 0x1f)); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (member_size == (DATA_CHAR & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, member_pointer_depth || member_elem_size == (DATA_CHAR & 0x1f) ? " movzx edx, byte [%s + %d]\n" : " movsx rdx, byte [%s + %d]\n", reg, offset); + } else { + fprintf (state->ofp, member_pointer_depth || member_elem_size == (DATA_CHAR & 0x1f) ? " movzx edx, byte ptr [%s + %d]\n" : " movsx rdx, byte ptr [%s + %d]\n", reg, offset); + } + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " %s byte [%s + %d]\n" : " %s byte ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } + + } else if (member_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, member_pointer_depth || member_elem_size == (DATA_SHORT & 0x1f) ? " movzx edx, word [%s + %d]\n" : " movsx rdx, word [%s + %d]\n", reg, offset); + } else { + fprintf (state->ofp, member_pointer_depth || member_elem_size == (DATA_SHORT & 0x1f) ? " movzx edx, word ptr [%s + %d]\n" : " movsx rdx, word ptr [%s + %d]\n", reg, offset); + } + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " %s word [%s + %d]\n" : " %s word ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } + + } else if (member_qword) { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov rdx, qword [%s + %d]\n" : " mov rdx, qword ptr [%s + %d]\n", reg, offset); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " %s qword [%s + %d]\n" : " %s qword ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } + + } else { + + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " movsxd rdx, dword [%s + %d]\n" : " movsxd rdx, dword ptr [%s + %d]\n", reg, offset); + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s + %d]\n" : " %s dword ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset); + } + + } + + } else { + + if (member_size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movsbq %d(%%%s), %%rdx\n", offset, reg); + } else if (member_size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movswq %d(%%%s), %%rdx\n", offset, reg); + } else if (member_qword) { + fprintf (state->ofp, " movq %d(%%%s), %%rdx\n", offset, reg); + } else { + fprintf (state->ofp, " movslq %d(%%%s), %%rdx\n", offset, reg); + } + + if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) { + + if (member_size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %sb %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg); + } else if (member_size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %sw %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg); + } else if (member_qword) { + fprintf (state->ofp, " %sq %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg); + } else { + fprintf (state->ofp, " %sl %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg); + } + + } + + } + + } + + if (tok.kind == TOK_ASSIGN) { + + emit_push_reg_now ("rdx"); + get_token (); + + emit_load_assignment_rhs_expression_to_reg (reg); + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", reg, deref_size); + + return; + + } + + if (state->ofp && strcmp (reg, "rdx") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rdx\n", reg); + } else { + fprintf (state->ofp, " movq %%rdx, %%%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 ("rdx"); + 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 ("rdx"); + 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 ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + + emit_store_reg_to_deref_reg_now ("rdx", 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, " notq %%%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, "rax") != 0) { + + fprintf (state->ofp, " setz al\n"); + fprintf (state->ofp, " movzx eax, al\n"); + fprintf (state->ofp, " mov %s, rax\n", reg); + + } else { + + fprintf (state->ofp, " setz al\n"); + fprintf (state->ofp, " movzx eax, al\n"); + + } + + } else { + + fprintf (state->ofp, " testq %%%s, %%%s\n", reg, reg); + fprintf (state->ofp, " setz %%al\n"); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (reg, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%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) { + + const char *dreg = amd64_dword_reg_name_from_any (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", dreg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, dreg); + } + + } + + 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; + int64_s v; + + size_t len = tok.ident ? strlen (tok.ident) : 0; + + v.low = current_floating_token_is_nonzero_now () ? 1 : 0; + v.high = 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_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + get_token (); + + emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_deref_reg_now (reg, va_size); + + if (va_pointer > 0) { + set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); + } else { + + emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); + clear_rhs_last_pointer_info (); + + } + + 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 (); + + struct local_symbol *src; + int postfix_incdec = 0; + + get_token (); + + if (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_deref_reg_now (reg, va_size); + + if (va_pointer > 0) { + set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); + } else { + + emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); + clear_rhs_last_pointer_info (); + + } + + free (name); + return; + + } + + { + + 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 saw_subscript; + 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 && src->pointer_depth == 0 && elem_size <= (DATA_PTR & 0x1f)) { + + int aggregate_elem_size = aggregate_tag_size_or_zero (src->tag_name ? src->tag_name : src->pointed_tag_name); + + if (aggregate_elem_size > (DATA_PTR & 0x1f)) { + elem_size = aggregate_elem_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); + } + + saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, src->pointer_depth, src->pointed_size, src->array_dimensions, src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned); + + postfix_copy_lvalue_size = index_step_size (elem_size); + postfix_copy_lvalue_tag_name = src->is_array ? (src->tag_name ? src->tag_name : src->pointed_tag_name) : src->pointed_tag_name; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + postfix_copy_lvalue_tag_name = 0; + + 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { + + postfix_member_seen = 1; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = elem_size; + postfix_member_size = elem_size; + postfix_member_is_floating = src->is_floating; + postfix_member_is_unsigned = src->is_unsigned; + + } + + 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 saw_subscript; + int elem_size; + + { + + 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 (get_global_symbol_array (name) && get_global_symbol_pointer_depth (name) == 0 && elem_size <= (DATA_PTR & 0x1f)) { + + int aggregate_elem_size = aggregate_tag_size_or_zero (get_global_symbol_tag_name (name)); + + if (aggregate_elem_size > (DATA_PTR & 0x1f)) { + elem_size = aggregate_elem_size; + } + + } + + 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); + } + + saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name), get_global_symbol_array_dimensions (name), get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name)); + + postfix_copy_lvalue_size = index_step_size (elem_size); + postfix_copy_lvalue_tag_name = get_global_symbol_array (name) ? get_global_symbol_tag_name (name) : 0; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + postfix_copy_lvalue_tag_name = 0; + + 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { + + postfix_member_seen = 1; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = elem_size; + postfix_member_size = elem_size; + postfix_member_is_floating = get_global_symbol_floating (name); + postfix_member_is_unsigned = get_global_symbol_unsigned (name); + + } + + 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 ("rdx"); + 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 ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_member_to_addr_reg_now ("rdx", 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, " xorq %%%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_ex (reg, dst->static_label, dst->size, dst->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, dst->offset, dst->size, dst->is_unsigned); + } + + } else { + emit_load_global_to_reg_ex (reg, name, dst_size, get_global_symbol_unsigned (name)); + } + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + if (strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rax, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rax\n", reg); + } + + } + + emit_assignment_binary_op (assign_op, dst ? dst->is_unsigned : get_global_symbol_unsigned (name)); + + if (strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%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 ("rdx"); + emit_pop_reg_now (reg); + + emit_assignment_binary_op (assign_op, is_unsigned); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", 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_ex (reg, src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, src->offset, src->size, src->is_unsigned); + } + + 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_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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 = get_global_symbol_tag_name (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 (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, " xorq %%%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, " xorq %%%s, %%%s\n", reg, reg); + } + + } + + return; + + } + + { + + int64_s v = const64_from_current_operand (); + + if (state->ofp) { + + const char *dreg = amd64_dword_reg_name_from_any (reg); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %lu\n", dreg, v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, dreg); + } + + } + + } + +} + +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, " imulq $%d, %%%s, %%%s\n", scale, reg, reg); + } + +} + +static void emit_divide_rax_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, " cqo\n"); + fprintf (state->ofp, " idiv rcx\n"); + + } else { + + fprintf (state->ofp, " movl $%d, %%ecx\n", divisor); + fprintf (state->ofp, " cqto\n"); + fprintf (state->ofp, " idivq %%rcx\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 rax, rdx\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " sub rax, rdx\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imul rax, rdx\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " mov rcx, rdx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor rdx, rdx\n"); + fprintf (state->ofp, " div rcx\n"); + + } else { + + fprintf (state->ofp, " cqo\n"); + fprintf (state->ofp, " idiv rcx\n"); + + } + + break; + + case TOK_MOD: + + fprintf (state->ofp, " mov rcx, rdx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor rdx, rdx\n"); + fprintf (state->ofp, " div rcx\n"); + + } else { + + fprintf (state->ofp, " cqo\n"); + fprintf (state->ofp, " idiv rcx\n"); + + } + + fprintf (state->ofp, " mov rax, rdx\n"); + break; + + case TOK_MODEQ: + + fprintf (state->ofp, " mov rcx, rdx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xor rdx, rdx\n"); + fprintf (state->ofp, " div rcx\n"); + + } else { + + fprintf (state->ofp, " cqo\n"); + fprintf (state->ofp, " idiv rcx\n"); + + } + + fprintf (state->ofp, " mov rax, rdx\n"); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " and rax, rdx\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " or rax, rdx\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xor rax, rdx\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + fprintf (state->ofp, " mov rcx, rdx\n"); + fprintf (state->ofp, " shl rax, cl\n"); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + fprintf (state->ofp, " mov rcx, rdx\n"); + fprintf (state->ofp, is_unsigned ? " shr rax, cl\n" : " sar rax, cl\n"); + + break; + + default: + + break; + + } + + } else { + + switch (op) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " addq %%rdx, %%rax\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " subq %%rdx, %%rax\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " imulq %%rdx, %%rax\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorq %%rdx, %%rdx\n"); + fprintf (state->ofp, " divq %%rcx\n"); + + } else { + + fprintf (state->ofp, " cqto\n"); + fprintf (state->ofp, " idivq %%rcx\n"); + + } + + break; + + case TOK_MOD: + + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorq %%rdx, %%rdx\n"); + fprintf (state->ofp, " divq %%rcx\n"); + + } else { + + fprintf (state->ofp, " cqto\n"); + fprintf (state->ofp, " idivq %%rcx\n"); + + } + + fprintf (state->ofp, " movq %%rdx, %%rax\n"); + break; + + case TOK_MODEQ: + + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + + if (is_unsigned) { + + fprintf (state->ofp, " xorq %%rdx, %%rdx\n"); + fprintf (state->ofp, " divq %%rcx\n"); + + } else { + + fprintf (state->ofp, " cqto\n"); + fprintf (state->ofp, " idivq %%rcx\n"); + + } + + fprintf (state->ofp, " movq %%rdx, %%rax\n"); + break; + + case TOK_AMPER: case TOK_ANDEQ: + + fprintf (state->ofp, " andq %%rdx, %%rax\n"); + break; + + case TOK_PIPE: case TOK_OREQ: + + fprintf (state->ofp, " orq %%rdx, %%rax\n"); + break; + + case TOK_CARET: case TOK_XOREQ: + + fprintf (state->ofp, " xorq %%rdx, %%rax\n"); + break; + + case TOK_LSH: case TOK_LSHEQ: + + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + fprintf (state->ofp, " salq %%cl, %%rax\n"); + + break; + + case TOK_RSH: case TOK_RSHEQ: + + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + fprintf (state->ofp, is_unsigned ? " shrq %%cl, %%rax\n" : " sarq %%cl, %%rax\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 = (double) tok.val.ld; + } else { + f = 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 double floating_constant_to_ld_now (void) { + + double v; + + if (tok.kind == TOK_CFLOAT) { + v = (float) tok.val.f; + } else if (tok.kind == TOK_CLDOUBLE) { + v = (double) tok.val.ld; + } else { + v = tok.val.d; + } + + get_token (); + return v; + +} + +static double int64_u32_base_now (void) { + + volatile unsigned long half; + double d; + + /* + * Build 2^32 without a direct large floating literal. The volatile word + * prevents the self compiler from folding this back into an LC*_flt data + * constant while compiling parse.c. + */ + half = 65536UL; + + d = (double) half; + d *= (double) half; + + return d; + +} + +static double int64_to_double_now (int64_s v) { + + double d; + + d = (double) v.high; + d *= int64_u32_base_now (); + d += (double) v.low; + + return d; + +} + +static double parse_floating_const_expr_value_now (void); +static double parse_floating_const_term_now (void); +static double parse_floating_const_primary_now (void); + +static double parse_floating_const_primary_now (void) { + + 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 (); + return int64_to_double_now (iv); + +} + +static double parse_floating_const_term_now (void) { + + double rhs, v; + enum token_kind op; + + 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 double parse_floating_const_expr_value_now (void) { + + double rhs, v; + enum token_kind op; + + 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) { + + 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; + + } + + { + + unsigned char bytes[8]; + + double d = (double) acc; + int i; + + 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) { + + switch_section (SECTION_DATA); + + if (size == (DATA_DOUBLE & 0x1f)) { + + /* + * Emit the exact IEEE bits as a 64-bit hex integer. The old + * decimal concatenated the high and low dwords as text, and the + * high==0 branch emitted only a single dd even though the later + * load is fld qword ptr. Both forms are unstable across + * bootstrap runs. + */ + fprintf (state->ofp, "LC%d_flt dq 0%08lX%08lXh\n", lab, v.high & U32_MASK, v.low & U32_MASK); + + } else { + fprintf (state->ofp, "LC%d_flt dd 0%08lXh\n", lab, v.low & U32_MASK); + } + + switch_section (SECTION_TEXT); + fprintf (state->ofp, " fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + if (state->syntax & ASM_SYNTAX_NASM) { + + switch_section (SECTION_DATA); + + 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); + } + + switch_section (SECTION_TEXT); + fprintf (state->ofp, " fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + switch_section (SECTION_DATA); + + 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); + } + + switch_section (SECTION_TEXT); + + 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_rbp_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(%%rbp)\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_rbp_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(%%rbp)\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_rbp_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(%%rbp)\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_ex ("rax", src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex ("rax", src->offset, src->size, src->is_unsigned); + } + + } else { + emit_load_global_to_reg_ex ("rax", name, size, get_global_symbol_unsigned (name)); + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\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 int emit_statement_rhs_const32_to_rdx_if_possible (void); + +static void emit_statement_label (int label); +static void emit_statement_label_raw (int label); +static void emit_statement_jump (int label); + +static void emit_rax_bool_to_floating_stack_now (void) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\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_rax_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_floating_truth_to_rax_now (void) { + + if (!state->ofp) { + + floating_rhs_result_in_eax_bool = 1; + return; + + } + + /* + * A floating value used as a condition is true when it compares != 0.0. + * Testing the stored IEEE bits is wrong for values such as -0.0, whose + * bit pattern is non-zero but whose C truth value is false. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " fldz\n"); + } else { + fprintf (state->ofp, " fldz\n"); + } + + emit_floating_compare_to_rax_now (TOK_NOTEQ); + +} + +static void emit_fild_rax_now (void) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + +} + +static void emit_load_any_deref_as_floating_now (const char *reg, int size, int is_floating) { + + if (is_floating) { + + emit_load_floating_deref_reg_now (reg, size); + return; + + } + + emit_load_deref_reg_now (reg, size); + emit_fild_rax_now (); + +} + +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; + int deref_is_floating = 1; + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) { + + emit_load_any_deref_as_floating_now ("rax", deref_size, last_deref_cast_type_is_floating); + return; + + } + + emit_load_assignment_rhs_expression_to_reg ("rax"); + expect (TOK_RPAREN, ")"); + + if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { + + deref_size = rhs_last_pointed_size; + deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); + + } + + emit_load_any_deref_as_floating_now ("rax", deref_size, deref_is_floating); + return; + + } + + emit_load_assignment_rhs_to_reg ("rax"); + + if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { + + deref_size = rhs_last_pointed_size; + deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); + + } + + emit_load_any_deref_as_floating_now ("rax", deref_size, deref_is_floating); + 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)) { + + if (!cast_is_pointer && !last_cast_type_is_floating) { + + if (rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_operand_now (result_size); + + if (floating_rhs_result_in_eax_bool) { + + emit_extend_pair_high_from_low ("rax", "rdx", cast_size, cast_is_unsigned); + floating_rhs_result_in_eax_bool = 0; + + } else { + emit_floating_stack_to_int_pair_now ("rax", "rdx"); + } + + if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { + emit_extend_pair_high_from_low ("rax", "rdx", cast_size, cast_is_unsigned); + } + + } else { + emit_load_assignment_rhs_to_pair ("rax", "rdx"); + } + + emit_integer_pair_to_floating_stack_now ("rax", "rdx", cast_size); + return; + + } + + 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_rax_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 rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl $%lu, (%%rsp)\n", v.low & U32_MASK); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + return; + + } + + if (tok.kind == TOK_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_DOUBLE & 0x1f; + int va_unsigned = 0; + int va_pointer = 0; + int va_floating = 0; + + get_token (); + + emit_parse_builtin_va_arg_address_to_reg_now ("rax", &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_floating_deref_reg_now ("rax", va_size); + + 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_DOUBLE & 0x1f; + int va_unsigned = 0; + int va_pointer = 0; + int va_floating = 0; + + emit_parse_builtin_va_arg_address_to_reg_now ("rax", &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_floating_deref_reg_now ("rax", va_size); + + free (name); + return; + + } + + 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, "rax", name_start, name_caret, name_line); + + if (!get_global_function_returns_floating (name)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + } else { + amd64_emit_load_xmm0_to_floating_stack_now (get_global_symbol_size (name)); + } + + free (name); + return; + + } + + if (tok.kind == TOK_LBRACK && (src || find_global_symbol (name) >= 0)) { + + int elem_size; + int pointer_depth; + int pointed_size; + + 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); + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + if (src->is_array) { + + if (src->is_static && src->static_label) { + emit_load_symbol_address_to_reg_now ("rax", src->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now ("rax", 0, src->offset, 1); + } + + } else if (src->is_static && src->static_label) { + emit_load_global_to_reg ("rax", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", src->offset, DATA_PTR); + } + + } 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)); + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + if (get_global_symbol_array (name)) { + emit_load_symbol_address_to_reg_now ("rax", name, 0, 0); + } else { + emit_load_global_to_reg ("rax", name, DATA_PTR); + } + + } + + if (elem_size <= 0) { + elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscripts_to_reg_now ("rax", elem_size, pointer_depth, pointed_size); + + if ((elem_size & 0x1f) > DATA_PTR) { + emit_load_floating_deref_reg_now ("rax", elem_size); + } else { + emit_fild_rax_now (); + } + + 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 ("rax", src->static_label, DATA_PTR & 0x1f); + } else { + emit_load_local_to_reg ("rax", src->offset, DATA_PTR & 0x1f); + } + + } else { + emit_load_global_to_reg ("rax", name, DATA_PTR & 0x1f); + } + + if (member_is_floating) { + emit_load_floating_member_from_addr_reg_now ("rax", member_offset, member_size); + } else { + + emit_load_member_from_addr_reg_now ("rax", "rax", member_offset, member_size); + emit_rax_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; + const char *current_tag_name; + + 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 current_size; + + if (src) { + + current_size = src->size; + current_tag_name = src->tag_name; + + } else { + + current_size = get_global_symbol_size (name); + current_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, current_size, current_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 (tok.kind != TOK_DOT && tok.kind != TOK_ARROW && member_is_floating) { + + emit_load_floating_member_symbol_now (src, name, member_offset, member_size); + + free (name); + return; + + } + + emit_load_symbol_address_for_copy_now ("rax", src, name); + emit_add_const_to_reg_now ("rax", member_offset); + + current_tag_name = last_found_member_tag_name; + + if (member_pointer_depth > 0 || member_is_array) { + current_size = member_elem_size; + } else { + current_size = member_size; + } + + while (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) { + + enum token_kind 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; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_size, current_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 (member_op == TOK_ARROW) { + emit_load_deref_reg_now ("rax", DATA_PTR & 0x1f); + } + + emit_add_const_to_reg_now ("rax", member_offset); + + current_tag_name = last_found_member_tag_name; + + if (member_pointer_depth > 0 || member_is_array) { + current_size = member_elem_size; + } else { + current_size = member_size; + } + + } + + if (member_is_floating) { + emit_load_floating_member_from_addr_reg_now ("rax", 0, member_size); + } else { + + emit_load_deref_reg_now ("rax", member_size); + emit_rax_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_ex ("rax", src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex ("rax", src->offset, src->size, src->is_unsigned); + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\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_ex ("rax", name, get_global_symbol_size (name), get_global_symbol_unsigned (name)); + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], eax\n"); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], eax\n"); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%eax, (%%rsp)\n"); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\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 rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " mov dword [rsp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword [rsp]\n"); + + } else { + + fprintf (state->ofp, " mov dword ptr [rsp], %lu\n", v.low & U32_MASK); + fprintf (state->ofp, " fild dword ptr [rsp]\n"); + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl $%lu, (%%rsp)\n", v.low & U32_MASK); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\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) { + + if (!state->ofp) { + return; + } + + switch (k) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " faddp\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " fsubrp\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " fmulp\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " fdivrp\n"); + 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_rax_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_rax_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_rax_now (op); + + } + + if (tok.kind == TOK_QMARK) { + + false_label = anon_label++; + end_label = anon_label++; + + if (!floating_rhs_result_in_eax_bool) { + emit_floating_truth_to_rax_now (); + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " test rax, rax\n"); + } else { + fprintf (state->ofp, " testq %%rax, %%rax\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 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 DATA_LLONG 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 >= 8) { + + chunk = 8; + offset -= 8; + + } else 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 == 8) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push qword [%s + %d]\n" : " push qword ptr [%s + %d]\n"), asm_global_symbol_name (sym->static_label), offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword [%s + %d]\n", asm_global_symbol_name (sym->static_label), offset); + fprintf (state->ofp, " mov qword [rsp], rax\n"); + + } else { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword ptr [%s + %d]\n", asm_global_symbol_name (sym->static_label), offset); + fprintf (state->ofp, " mov qword ptr [rsp], rax\n"); + + } + + } 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 rax\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 rax\n"); + } + + } else { + + format_intel_rbp_offset (memref, sizeof (memref), sym->offset + offset); + + if (chunk == 8) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push qword %s\n" : " push qword ptr %s\n"), memref); + } else if (chunk == 4) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword %s\n", memref); + fprintf (state->ofp, " mov qword [rsp], rax\n"); + + } else { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword ptr %s\n", memref); + fprintf (state->ofp, " mov qword ptr [rsp], rax\n"); + + } + + } 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 rax\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 rax\n"); + } + + } + + } else { + + if (sym->is_static && sym->static_label) { + + if (chunk == 8) { + fprintf (state->ofp, " pushq %s+%d(%%rip)\n", asm_global_symbol_name (sym->static_label), offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + + fprintf (state->ofp, " xorq %%rax, %%rax\n"); + fprintf (state->ofp, " movl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (sym->static_label), offset); + fprintf (state->ofp, " movq %%rax, (%%rsp)\n"); + + } else if (chunk == 2) { + fprintf (state->ofp, " movzwl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushq %%rax\n"); + } else { + fprintf (state->ofp, " movzbl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushq %%rax\n"); + } + + } else { + + if (chunk == 8) { + fprintf (state->ofp, " pushq %ld(%%rbp)\n", sym->offset + offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + + fprintf (state->ofp, " xorq %%rax, %%rax\n"); + fprintf (state->ofp, " movl %ld(%%rbp), %%eax\n", sym->offset + offset); + fprintf (state->ofp, " movq %%rax, (%%rsp)\n"); + + } else if (chunk == 2) { + fprintf (state->ofp, " movzwl %ld(%%rbp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushq %%rax\n"); + } else { + fprintf (state->ofp, " movzbl %ld(%%rbp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushq %%rax\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 >= 8) { + + chunk = 8; + offset -= 8; + + } else 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 == 8) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? + " push qword [%s + %d]\n" : + " push qword ptr [%s + %d]\n"), asm_global_symbol_name (name), offset); + + } else if (chunk == 4) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (state->syntax & ASM_SYNTAX_NASM) { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword [%s + %d]\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " mov qword [rsp], rax\n"); + + } else { + + fprintf (state->ofp, " xor rax, rax\n"); + fprintf (state->ofp, " mov eax, dword ptr [%s + %d]\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " mov qword ptr [rsp], rax\n"); + + } + + } 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 rax\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 rax\n"); + + } + + } else { + + if (chunk == 8) { + fprintf (state->ofp, " pushq %s+%d(%%rip)\n", asm_global_symbol_name (name), offset); + } else if (chunk == 4) { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + + fprintf (state->ofp, " xorq %%rax, %%rax\n"); + fprintf (state->ofp, " movl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " movq %%rax, (%%rsp)\n"); + + } else if (chunk == 2) { + + fprintf (state->ofp, " movzwl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " pushq %%rax\n"); + + } else { + + fprintf (state->ofp, " movzbl %s+%d(%%rip), %%eax\n", asm_global_symbol_name (name), offset); + fprintf (state->ofp, " pushq %%rax\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; + + int amd64_call_stack_bytes = 0; + int saved_pointer_offset = 0; + + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps = 0; + + int *arg_tmp_floating = 0; + int *new_arg_tmp_floating = 0; + int *arg_tmp_float_sizes = 0; + int *new_arg_tmp_float_sizes = 0; + + int arg_saved_stack_mod16 = 0; + + FILE *arg_saved_ofp = 0; + FILE *arg_tmp_ofp = 0; + + int saved_arg_assignment32_stop_before_condition_operator; + int saved_arg_assignment64_stop_before_condition_operator; + + if (tok.kind != TOK_LPAREN) { + return; + } + + if (state->ofp) { + amd64_emit_push_reg_now (fn_reg); + } + + saved_arg_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + saved_arg_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; + + assignment32_stop_before_condition_operator = 0; + assignment64_stop_before_condition_operator = 0; + + get_token (); + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + arg_saved_ofp = 0; + arg_tmp_ofp = 0; + arg_is_floating = 0; + arg_bytes = DATA_PTR; + + if (state->ofp) { + + arg_tmp_ofp = scc_tmpfile (); + + if (arg_tmp_ofp) { + + arg_saved_stack_mod16 = amd64_temp_stack_mod16; + arg_saved_ofp = state->ofp; + + amd64_temp_stack_mod16 = 0; + 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 : DATA_PTR; + arg_is_floating = 0; + + get_token (); + + } else if (tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && !find_local_symbol (tok.ident) && emit_push_global_aggregate_argument_now (tok.ident)) { + + arg_bytes = get_global_symbol_size (tok.ident); + arg_is_floating = 0; + + get_token (); + + } else if (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_rsp_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, "rax", 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 (); + + /* + * A call through a function pointer currently has no recorded + * parameter prototype in this backend path. Treat it like an + * unprototyped call for floating arguments: C default argument + * promotions make float become double, and the Microsoft x64 + * ABI then passes that promoted double in XMMn plus the matching + * GP register. Fixed float parameters are handled by the + * direct-call prototype path, not here. + */ + arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR; + + /* + * Function-pointer calls do not have a direct prototype in + * the global-symbol table, but the AMD64 ABI still passes + * integer/pointer arguments in 64-bit register/stack slots. + * The generic scalar loader may leave a long long expression + * in the old i386 EDX:EAX form or otherwise only materialise + * the low half. Match the normal-call path for source + * arguments that are visibly 64-bit integers so replaying the + * temporary argument block can move the complete value from + * RAX into RCX/RDX/R8/R9 or the overflow stack slot. + */ + if (!arg_is_floating && current_argument_starts_64bit_integer_now ()) { + + arg_bytes = DATA_LLONG & 0x1f; + amd64_emit_load_assignment_rhs_expression_to_rax64 (rhs_current_operand_is_unsigned_now ()); + + } else if (arg_is_floating) { + emit_load_floating_rhs_expression_now (arg_bytes); + } else { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + 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 ("rax", 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 ("rax", arg_bytes); + + } else if (arg_is_floating) { + /* Keep ST(0) live; amd64_emit_move_st0_to_arg() places it. */ + } else { + /* Keep RAX live; the AMD64 call replay moves it to the proper slot. */ + } + + } + + } + + total_arg_bytes += arg_bytes; + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + + amd64_temp_stack_mod16 = arg_saved_stack_mod16; + state->ofp = arg_saved_ofp; + + new_arg_tmp_ofps = (FILE **) xrealloc (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; + + } + + new_arg_tmp_floating = (int *) xrealloc (arg_tmp_floating, sizeof (*arg_tmp_floating) * (argc + 1)); + + if (new_arg_tmp_floating) { + + arg_tmp_floating = new_arg_tmp_floating; + arg_tmp_floating[argc] = arg_is_floating; + + } + + new_arg_tmp_float_sizes = (int *) xrealloc (arg_tmp_float_sizes, sizeof (*arg_tmp_float_sizes) * (argc + 1)); + + if (new_arg_tmp_float_sizes) { + + arg_tmp_float_sizes = new_arg_tmp_float_sizes; + arg_tmp_float_sizes[argc] = arg_is_floating ? arg_bytes : 0; + + } + + } + + if (arg_tmp_ofp) { + + scc_close (arg_tmp_ofp); + arg_tmp_ofp = 0; + + } + + argc++; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + assignment32_stop_before_condition_operator = saved_arg_assignment32_stop_before_condition_operator; + assignment64_stop_before_condition_operator = saved_arg_assignment64_stop_before_condition_operator; + + if (state->ofp) { + + /* + * The function pointer itself was saved before evaluating arguments, + * because argument evaluation is allowed to clobber its register. + * For the Microsoft x64 ABI, reserve the 32-byte home area plus any + * overflow argument slots, then replay each argument left-to-right into + * RCX/RDX/R8/R9 or the overflow stack area. + */ + amd64_call_stack_bytes = 32; + + if (argc > 4) { + amd64_call_stack_bytes += (argc - 4) * 8; + } + + /* + * The saved function pointer is already on the stack above this + * reservation. Account for that extra 8-byte push when choosing the + * call-frame size; otherwise indirect calls enter with RSP 8 bytes off + * the Microsoft x64 16-byte call-site alignment. + */ + amd64_call_stack_bytes = amd64_align_call_stack_bytes (amd64_call_stack_bytes); + amd64_emit_sub_rsp_bytes (amd64_call_stack_bytes); + + for (i = 0; i < argc; 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); + } + + if (arg_tmp_floating && arg_tmp_floating[i]) { + amd64_emit_move_st0_to_arg (i, (arg_tmp_float_sizes && arg_tmp_float_sizes[i]) ? arg_tmp_float_sizes[i] : (DATA_DOUBLE & 0x1f), 1); + } else if (i < 4) { + amd64_emit_store_rax_to_home_arg (i); + } else { + amd64_emit_store_rax_to_stack_arg (i - 4); + } + + scc_close (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + for (i = 0; i < argc && i < 4; i++) { + + if (!(arg_tmp_floating && arg_tmp_floating[i])) { + amd64_emit_load_home_arg_to_reg (i); + } + + } + + if (arg_tmp_ofps) { + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + if (arg_tmp_floating) { + + free (arg_tmp_floating); + arg_tmp_floating = 0; + + } + + if (arg_tmp_float_sizes) { + + free (arg_tmp_float_sizes); + arg_tmp_float_sizes = 0; + + } + + saved_pointer_offset = amd64_call_stack_bytes; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " mov r11, qword [rsp + %d]\n", saved_pointer_offset); + } else { + fprintf (state->ofp, " mov r11, qword ptr [rsp + %d]\n", saved_pointer_offset); + } + + fprintf (state->ofp, " call r11\n"); + amd64_emit_add_rsp_bytes (amd64_call_stack_bytes + (DATA_PTR & 0x1f)); + + if (strcmp (result_reg, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", result_reg); + } + + } else { + + fprintf (state->ofp, " movq %d(%%rsp), %%r11\n", saved_pointer_offset); + fprintf (state->ofp, " call *%%r11\n"); + + amd64_emit_add_rsp_bytes (amd64_call_stack_bytes + (DATA_PTR & 0x1f)); + + if (strcmp (result_reg, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%s\n", result_reg); + } + + } + + } + + if (arg_tmp_ofps) { + + for (i = 0; i < argc; i++) { + + if (arg_tmp_ofps[i]) { + scc_close (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + + } + + if (arg_tmp_floating) { + free (arg_tmp_floating); + } + + if (arg_tmp_float_sizes) { + free (arg_tmp_float_sizes); + } + +} + +static void emit_sub_rsp_now (int bytes) { + amd64_emit_sub_rsp_bytes (bytes); +} + +static void emit_load_pending_struct_return_address_to_rax_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 rax, [rsp + %d]\n", stack_arg_bytes); + } else { + fprintf (state->ofp, " lea rax, [rsp + %d]\n", stack_arg_bytes); + } + + } else { + fprintf (state->ofp, " leaq %d(%%rsp), %%rax\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 rax, qword [rsp + %d]\n", stack_arg_bytes); + } else { + fprintf (state->ofp, " mov rax, qword ptr [rsp + %d]\n", stack_arg_bytes); + } + + if (pending_struct_return_stack_offset) { + fprintf (state->ofp, " add rax, %d\n", pending_struct_return_stack_offset); + } + + } else { + + fprintf (state->ofp, " movq %d(%%rsp), %%rax\n", stack_arg_bytes); + + if (pending_struct_return_stack_offset) { + fprintf (state->ofp, " addq $%d, %%rax\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 ("rax", pending_struct_return_lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("rax", pending_struct_return_lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("rax", pending_struct_return_global_name); + } + +} + + + +static int current_argument_is_bare_64bit_identifier_now (void) { + + struct local_symbol *sym; + + if (tok.kind != TOK_IDENT || !tok.ident || !current_argument_is_bare_identifier_now ()) { + return 0; + } + + sym = find_local_symbol (tok.ident); + + if (sym) { + return sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating; + } + + if (find_global_symbol (tok.ident) >= 0) { + return get_global_symbol_size (tok.ident) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (tok.ident); + } + + return 0; + +} + +static int argument_text_skip_quoted_literal_now (const char **pp); + +static int argument_text_skip_balanced_parens_now (const char **pp) { + + const char *p; + int depth = 0; + + if (!pp || !*pp || **pp != '(') { + return 0; + } + + p = *pp; + + while (*p) { + + if (*p == '\'' || *p == '"') { + + argument_text_skip_quoted_literal_now (&p); + continue; + + } + + if (*p == '(') { + depth++; + } else if (*p == ')') { + + depth--; + + if (depth == 0) { + + *pp = p + 1; + return 1; + + } + + } + + p++; + + } + + return 0; + +} + +static int argument_text_skip_quoted_literal_now (const char **pp) { + + const char *p; + char quote; + + if (!pp || !*pp || (**pp != '\'' && **pp != '"')) { + return 0; + } + + p = *pp; + quote = *p++; + + while (*p) { + + if (*p == '\\') { + + p++; + + if (*p) { + p++; + } + + continue; + + } + + if (*p == quote) { + + *pp = p + 1; + return 1; + + } + + p++; + + } + + *pp = p; + return 1; + +} + +static int argument_text_mentions_64bit_symbol_now (const char *p) { + + char name[256]; + int depth = 0; + int i; + + if (!p) { + return 0; + } + + while (*p) { + + if (depth == 0 && *p == ',') { + return 0; + } + + if (depth == 0 && (*p == ')' || *p == ';' || *p == '{' || *p == '}')) { + return 0; + } + + if (*p == '\'' || *p == '"') { + + argument_text_skip_quoted_literal_now (&p); + continue; + + } + + if (*p == '(' || *p == '[') { + + depth++; + p++; + + continue; + + } + + if (*p == ')' || *p == ']') { + + if (depth <= 0) { + return 0; + } + + depth--; + p++; + + continue; + + } + + 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++; + } + + /* + * Do not let long-long arguments of a nested call upgrade the + * enclosing argument. For example, in: + * + * fp (g (ll)); + * + * the argument passed to fp has the type returned by g(), not the + * type of ll. The old text scan walked through the inner call's + * argument list and treated the outer argument as a visible + * long-long expression. Only the nested call's own return type is + * relevant here; its parameters are not. + */ + if (*p == '(') { + + if (find_global_symbol (name) >= 0 && + get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && + get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && + !get_global_symbol_floating (name)) { + return 1; + } + + if (!argument_text_skip_balanced_parens_now (&p)) { + return 0; + } + + continue; + + } + + 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 argument_text_current_argument_end_now (const char *p, const char **endp) { + + const char *q; + int depth = 0; + + if (!p || !endp) { + return 0; + } + + q = p; + + while (*q) { + + if (depth == 0 && (*q == ',' || *q == ';' || *q == '{' || *q == '}')) { + + *endp = q; + return 1; + + } + + if (depth == 0 && *q == ')') { + + *endp = q; + return 1; + + } + + if (*q == '\'' || *q == '"') { + + const char *literal = q; + + argument_text_skip_quoted_literal_now (&literal); + q = literal; + + continue; + + } + + if (*q == '(' || *q == '[') { + depth++; + } else if (*q == ')' || *q == ']') { + + if (depth <= 0) { + + *endp = q; + return 1; + + } + + depth--; + + } + + q++; + + } + + *endp = q; + return 1; + +} + +static int argument_text_strip_outer_parens_now (const char **startp, const char **endp) { + + const char *p; + const char *q; + const char *end; + int depth; + + if (!startp || !*startp || !endp || !*endp) { + return 0; + } + + p = *startp; + end = *endp; + + while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + p++; + } + + while (end > p && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) { + end--; + } + + if (p >= end || *p != '(') { + return 0; + } + + depth = 0; + q = p; + + while (q < end) { + + if (*q == '\'' || *q == '"') { + + argument_text_skip_quoted_literal_now (&q); + continue; + + } + + if (*q == '(') { + depth++; + } else if (*q == ')') { + + depth--; + + if (depth == 0) { + + q++; + + while (q < end && (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n')) { + q++; + } + + if (q == end) { + + *startp = p + 1; + *endp = end - 1; + + return 1; + + } + + return 0; + + } + + } + + q++; + + } + + return 0; + +} + +static int argument_text_has_int_result_operator_now (const char *p) { + + const char *start; + const char *end; + int depth = 0; + + if (!p || !argument_text_current_argument_end_now (p, &end)) { + return 0; + } + + while (argument_text_strip_outer_parens_now (&p, &end)) { + ; + } + + start = p; + + while (p < end) { + + if (*p == '\'' || *p == '"') { + + argument_text_skip_quoted_literal_now (&p); + continue; + + } + + if (*p == '(' || *p == '[') { + + depth++; + p++; + + continue; + + } + + if (*p == ')' || *p == ']') { + + if (depth > 0) { + depth--; + } + + p++; + continue; + + } + + if (depth == 0) { + + if ((p + 1) < end && ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '&' && p[1] == '&') || + (p[0] == '|' && p[1] == '|') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '='))) { + return 1; + } + + if (*p == '<') { + + if (!((p + 1) < end && p[1] == '<') && !(p > start && p[-1] == '<')) { + return 1; + } + + } + + if (*p == '>') { + + if (!((p + 1) < end && p[1] == '>') && !(p > start && p[-1] == '>') && !(p > start && p[-1] == '-')) { + return 1; + } + + } + + if (*p == '!') { + + if (!((p + 1) < end && p[1] == '=')) { + return 1; + } + + } + + } + + p++; + + } + + return 0; + +} + +static int current_argument_has_int_result_operator_now (void) { + + if (argument_text_has_int_result_operator_now (tok.start)) { + return 1; + } + + if (argument_text_has_int_result_operator_now (tok.caret)) { + return 1; + } + + return 0; + +} + +static char *argument_text_copy_range_now (const char *start, const char *end) { + + char *out; + size_t len; + + if (!start || !end || end < start) { + return 0; + } + + 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); + out = xmalloc (len + 1); + + if (len > 0) { + memcpy (out, start, len); + } + + out[len] = 0; + return out; + +} + +static int argument_text_range_mentions_64bit_symbol_now (const char *start, const char *end) { + + char *copy; + int result; + + copy = argument_text_copy_range_now (start, end); + + if (!copy) { + return 0; + } + + result = argument_text_mentions_64bit_symbol_now (copy); + free (copy); + + return result; + +} + +static int argument_text_range_starts_64bit_cast_now (const char *start, const char *end) { + + char *copy; + int result; + + copy = argument_text_copy_range_now (start, end); + + if (!copy) { + return 0; + } + + result = argument_text_starts_64bit_cast_now (copy); + free (copy); + + return result; + +} + +static int argument_text_range_has_int_result_operator_now (const char *start, const char *end) { + + char *copy; + int result; + + copy = argument_text_copy_range_now (start, end); + + if (!copy) { + return 0; + } + + result = argument_text_has_int_result_operator_now (copy); + free (copy); + + return result; + +} + +static int argument_text_conditional_result_is_64bit_now (const char *p, int *seen_conditional) { + + const char *start; + const char *end; + const char *qmark = 0; + const char *colon = 0; + int depth = 0; + + if (seen_conditional) { + *seen_conditional = 0; + } + + if (!p || !argument_text_current_argument_end_now (p, &end)) { + return 0; + } + + while (argument_text_strip_outer_parens_now (&p, &end)) { + ; + } + + start = p; + + while (p < end) { + + if (*p == '\'' || *p == '"') { + + argument_text_skip_quoted_literal_now (&p); + continue; + + } + + if (*p == '(' || *p == '[') { + + depth++; + p++; + + continue; + + } + + if (*p == ')' || *p == ']') { + + if (depth > 0) { + depth--; + } + + p++; + continue; + + } + + if (depth == 0 && *p == '?') { + + qmark = p; + break; + + } + + p++; + + } + + if (!qmark) { + return 0; + } + + if (seen_conditional) { + *seen_conditional = 1; + } + + p = qmark + 1; + depth = 0; + + while (p < end) { + + if (*p == '\'' || *p == '"') { + + argument_text_skip_quoted_literal_now (&p); + continue; + + } + + if (*p == '(' || *p == '[') { + + depth++; + p++; + + continue; + + } + + if (*p == ')' || *p == ']') { + + if (depth > 0) { + depth--; + } + + p++; + continue; + + } + + if (depth == 0 && *p == ':') { + + colon = p; + break; + + } + + p++; + + } + + if (!colon) { + return 0; + } + + /* + * The condition expression controls only the branch selection. It does + * not decide the value type of the conditional operator. Do not let a + * long-long used only in the condition make an unprototyped/function- + * pointer call replay the whole argument as 64-bit: + * + * fp (ll ? 1 : 2); + * + * Only the second and third operands decide whether the conditional + * result itself needs 64-bit integer handling. + */ + if (argument_text_range_starts_64bit_cast_now (qmark + 1, colon) || + argument_text_range_starts_64bit_cast_now (colon + 1, end)) { + return 1; + } + + if ((!argument_text_range_has_int_result_operator_now (qmark + 1, colon) && + argument_text_range_mentions_64bit_symbol_now (qmark + 1, colon)) || + (!argument_text_range_has_int_result_operator_now (colon + 1, end) && + argument_text_range_mentions_64bit_symbol_now (colon + 1, end))) { + return 1; + } + + (void) start; + return 0; + +} + +static int current_argument_conditional_result_is_64bit_now (int *seen_conditional) { + + if (argument_text_conditional_result_is_64bit_now (tok.start, seen_conditional)) { + return 1; + } + + if (seen_conditional && *seen_conditional) { + return 0; + } + + if (argument_text_conditional_result_is_64bit_now (tok.caret, seen_conditional)) { + return 1; + } + + return 0; + +} + +static int current_argument_mentions_64bit_symbol_now (void) { + + if (argument_text_mentions_64bit_symbol_now (tok.start)) { + return 1; + } + + if (argument_text_mentions_64bit_symbol_now (tok.caret)) { + return 1; + } + + return 0; + +} + +static int argument_text_starts_64bit_cast_now (const char *p) { + + int saw_long = 0; + char word[32]; + int n; + const char *inner; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + /* + * The current token for an explicitly-cast argument can be the grouping + * parenthesis rather than the cast parenthesis itself, for example: + * + * fp (((long long) i)); + * + * The previous check only accepted text whose first '(' directly opened + * the cast type, so extra harmless grouping caused the indirect-call + * argument classifier to miss the 64-bit cast and replay only the low + * scalar value. Peel grouping parentheses while the next non-space + * character is another '(', but leave the normal cast parser below to + * validate that the inner construct is actually "long long". + */ + inner = p + 1; + + while (*inner == ' ' || *inner == '\t' || *inner == '\r' || *inner == '\n') { + inner++; + } + + if (*inner == '(' && argument_text_starts_64bit_cast_now (inner)) { + return 1; + } + + p++; + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == ')') { + return saw_long >= 2; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + n = 0; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '_') { + + if (n + 1 < (int) sizeof (word)) { + word[n++] = *p; + } + + p++; + + } + + word[n] = 0; + + if (strcmp (word, "unsigned") == 0) { + /* accepted as part of a cast prefix */ + } else if (strcmp (word, "signed") == 0) { + /* accepted as part of a cast prefix */ + } else if (strcmp (word, "const") == 0 || strcmp (word, "volatile") == 0) { + /* qualifiers before the long long are harmless here */ + } else if (strcmp (word, "long") == 0) { + saw_long++; + } else { + return 0; + } + + } + +} + +static int current_argument_starts_64bit_cast_now (void) { + + if (argument_text_starts_64bit_cast_now (tok.start)) { + return 1; + } + + if (argument_text_starts_64bit_cast_now (tok.caret)) { + return 1; + } + + return 0; + +} + +static int argument_text_starts_narrow_integer_cast_now (const char *p) { + + int saw_long = 0; + int saw_narrow = 0; + char word[32]; + int n; + const char *inner; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + inner = p + 1; + + while (*inner == ' ' || *inner == '\t' || *inner == '\r' || *inner == '\n') { + inner++; + } + + if (*inner == '(' && argument_text_starts_narrow_integer_cast_now (inner)) { + return 1; + } + + p++; + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == ')') { + return saw_narrow || saw_long == 1; + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + return 0; + } + + n = 0; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '_') { + + if (n + 1 < (int) sizeof (word)) { + word[n++] = *p; + } + + p++; + + } + + word[n] = 0; + + if (strcmp (word, "unsigned") == 0) { + /* accepted as part of a cast prefix */ + } else if (strcmp (word, "signed") == 0) { + /* accepted as part of a cast prefix */ + } else if (strcmp (word, "const") == 0 || strcmp (word, "volatile") == 0) { + /* qualifiers before the integer type are harmless here */ + } else if (strcmp (word, "char") == 0 || strcmp (word, "short") == 0 || strcmp (word, "int") == 0) { + saw_narrow = 1; + } else if (strcmp (word, "long") == 0) { + + saw_long++; + + if (saw_long >= 2) { + return 0; + } + + } else { + return 0; + } + + } + +} + +static int current_argument_starts_narrow_integer_cast_now (void) { + + if (argument_text_starts_narrow_integer_cast_now (tok.start)) { + return 1; + } + + if (argument_text_starts_narrow_integer_cast_now (tok.caret)) { + return 1; + } + + return 0; + +} + +static int current_argument_starts_64bit_integer_now (void) { + + if (tok.kind == TOK_CLLONG || tok.kind == TOK_CULLONG) { + return 1; + } + + if (current_argument_is_bare_64bit_identifier_now ()) { + return 1; + } + + if (current_argument_starts_64bit_cast_now ()) { + return 1; + } + + /* + * A visible cast to a narrower integer type overrides any long-long + * operand inside the cast expression. For example: + * + * fp ((int) ll); + * fp (((unsigned long) ll)); + * + * are passed as normal scalar integer arguments, not as 64-bit long-long + * arguments, even though the source text mentions a long-long object. + */ + if (current_argument_starts_narrow_integer_cast_now ()) { + return 0; + } + + { + + int seen_conditional = 0; + + if (current_argument_conditional_result_is_64bit_now (&seen_conditional)) { + return 1; + } + + if (seen_conditional) { + return 0; + } + + } + + /* + * A long-long operand inside a comparison or logical expression does not + * make the argument itself a long long. Expressions such as + * + * fp (ll == 0); + * fp ((ll < 0)); + * fp (!ll); + * + * produce int results, so do not let the later symbol scan upgrade them + * to 64-bit arguments for unprototyped/function-pointer calls. + * + * Check this after the conditional-operator special case. A conditional + * expression such as + * + * fp (c ? ll : 0); + * + * is still a 64-bit argument even though one branch may contain an + * int-producing subexpression in more complicated cases. + */ + if (current_argument_has_int_result_operator_now ()) { + return 0; + } + + /* + * Function-pointer calls do not have a prototype table entry to tell the + * argument parser that an expression such as "a + b" must be evaluated as + * a 64-bit integer when either side is long long. The previous check only + * caught bare identifiers and long-long constants, so indirect calls could + * still replay only the low 32 bits for non-trivial long-long arguments. + * Limit the source scan to the current comma-separated argument so a later + * long-long argument does not accidentally upgrade an earlier int argument. + */ + return current_argument_mentions_64bit_symbol_now (); + +} + +static const char *amd64_integer_arg_reg (int index) { + + static const char *regs[] = { "rcx", "rdx", "r8", "r9" }; + + if (index < 0 || index >= 4) { + return 0; + } + + return regs[index]; + +} + +static void amd64_emit_push_reg_now (const char *reg) { + emit_push_reg_now (reg); +} + +static void amd64_emit_pop_reg_now (const char *reg) { + emit_pop_reg_now (reg); +} + +static int amd64_align_call_stack_bytes (int bytes) { + + if (bytes < 0) { + bytes = 0; + } + + while (((bytes + amd64_temp_stack_mod16) & 15) != 0) { + bytes += 8; + } + + return bytes; + +} + +static void amd64_emit_sub_rsp_bytes (int bytes) { + + if (!state->ofp || bytes <= 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " sub rsp, %d\n", bytes); + } else { + fprintf (state->ofp, " subq $%d, %%rsp\n", bytes); + } + + amd64_note_stack_sub (bytes); + +} + +static void amd64_emit_add_rsp_bytes (int bytes) { + + if (!state->ofp || bytes <= 0) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add rsp, %d\n", bytes); + } else { + fprintf (state->ofp, " addq $%d, %%rsp\n", bytes); + } + + amd64_note_stack_add (bytes); + +} + +static void amd64_emit_store_rax_to_stack_arg (int slot) { + + int offset = 32 + slot * 8; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword [rsp + %d], rax\n" : " mov qword ptr [rsp + %d], rax\n"), offset); + } else { + fprintf (state->ofp, " movq %%rax, %d(%%rsp)\n", offset); + } + +} + +static void amd64_emit_store_rax_to_home_arg (int index) { + + int offset = index * 8; + + if (!state->ofp || index < 0 || index >= 4) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov qword [rsp + %d], rax\n" : " mov qword ptr [rsp + %d], rax\n"), offset); + } else { + fprintf (state->ofp, " movq %%rax, %d(%%rsp)\n", offset); + } + +} + +static void amd64_emit_load_home_arg_to_reg (int index) { + + const char *dst = amd64_integer_arg_reg (index); + int offset = index * 8; + + if (!state->ofp || !dst || index < 0 || index >= 4) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [rsp + %d]\n" : " mov %s, qword ptr [rsp + %d]\n"), dst, offset); + } else { + fprintf (state->ofp, " movq %d(%%rsp), %%%s\n", offset, dst); + } + +} + +static void amd64_emit_mov_imm64_to_rax (int64_s v) { + amd64_emit_mov_i64_to_qword_reg_now ("rax", v); +} + +static int amd64_emit_bare_64bit_identifier_to_rax_now (void) { + + struct local_symbol *sym; + char *name; + + if (tok.kind != TOK_IDENT || !tok.ident || !current_argument_is_bare_identifier_now ()) { + return 0; + } + + name = xstrdup (tok.ident); + sym = find_local_symbol (name); + + if (sym && sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) { + + if (sym->is_static && sym->static_label) { + emit_load_global64_to_rax (sym->static_label); + } else { + emit_load_local64_to_rax (sym->offset); + } + + get_token (); + + free (name); + return 1; + + } + + if (!sym && find_global_symbol (name) >= 0 && get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) { + + emit_load_global64_to_rax (name); + + get_token (); + free (name); + + return 1; + + } + + free (name); + return 0; + +} + +static void amd64_emit_load_assignment_rhs_expression_to_rax64 (int is_unsigned) { + + if (token_kind_is_integer_constant_now (tok.kind)) { + + struct token *saved_tok = clone_current_token_now (); + enum token_kind next_kind; + + get_token (); + + next_kind = tok.kind; + unget_token (saved_tok); + + if (!token_kind_is_binary_expression_operator_now (next_kind)) { + + int64_s v = const64_from_current_operand (); + amd64_emit_mov_imm64_to_rax (v); + + return; + + } + + } + + if (current_integer_expr_is_foldable_now ()) { + + int64_s v = const64_from_current_foldable_expr (); + amd64_emit_mov_imm64_to_rax (v); + + return; + + } + + if (amd64_emit_bare_64bit_identifier_to_rax_now ()) { + return; + } + + /* + * Do not fall back to the old i386-style high/low pair path here. + * The AMD64 scalar RHS loader is responsible for producing the final + * value in RAX using the real source type: qword for pointers/DATA_LLONG, + * dword-with-extension for int/long, and byte/word extension for smaller + * objects. + */ + emit_load_assignment_rhs_expression_to_reg ("rax"); + (void) is_unsigned; + +} + +static void amd64_emit_move_rax_to_arg_reg (int index) { + + const char *dst = amd64_integer_arg_reg (index); + + if (!state->ofp || !dst) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", dst); + } else { + fprintf (state->ofp, " movq %%rax, %%%s\n", dst); + } + +} + +static const char *amd64_xmm_arg_reg (int index) { + + static const char *regs[] = { "xmm0", "xmm1", "xmm2", "xmm3" }; + + if (index < 0 || index >= 4) { + return 0; + } + + return regs[index]; + +} + +static void amd64_emit_move_st0_to_arg (int index, int size, int duplicate_gp) { + + const char *xmm = amd64_xmm_arg_reg (index); + const char *ireg = amd64_integer_arg_reg (index); + int offset; + + if (!state->ofp) { + return; + } + + if (size != (DATA_FLOAT & 0x1f)) { + size = DATA_DOUBLE & 0x1f; + } + + if (index < 4 && xmm) { + + /* + * Microsoft x64 / EFI boundary rule: floating arguments are passed in + * XMM0-XMM3. The backend may still use x87 internally, but the call + * boundary must spill ST(0) to memory and reload the ABI register. + * + * A prototyped float parameter is a real 32-bit float in XMMn, not a + * double. The previous boundary shim always used fstp qword/movsd, + * which silently widened fixed float parameters and put the wrong ABI + * value in XMMn. Keep doubles on movsd, but use fstp dword/movss for + * fixed float slots. + * + * For varargs/unprototyped calls the same value must also be duplicated + * into the matching integer register so va_arg can recover it from the + * register home area. In normal C those arguments have already been + * default-promoted to double, so the duplicate path remains qword-sized + * for the ABI-visible vararg case. + * + * Always reserve an 8-byte temporary spill slot, even for float. Using + * a 4-byte sub/add pair temporarily misaligns RSP inside the already + * aligned call frame and makes this ABI shim harder to reason about. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + + if (size == (DATA_FLOAT & 0x1f)) { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp dword [rsp]\n" : " fstp dword ptr [rsp]\n")); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movss %s, dword [rsp]\n" : " movss %s, dword ptr [rsp]\n"), xmm); + + if (duplicate_gp && ireg) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp]\n" : " mov %s, dword ptr [rsp]\n"), amd64_dword_reg_name_from_any (ireg)); + } + + } else { + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [rsp]\n" : " fstp qword ptr [rsp]\n")); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movsd %s, qword [rsp]\n" : " movsd %s, qword ptr [rsp]\n"), xmm); + + if (duplicate_gp && ireg) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, qword [rsp]\n" : " mov %s, qword ptr [rsp]\n"), ireg); + } + + } + + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + + if (size == (DATA_FLOAT & 0x1f)) { + + fprintf (state->ofp, " fstps (%%rsp)\n"); + fprintf (state->ofp, " movss (%%rsp), %%%s\n", xmm); + + if (duplicate_gp && ireg) { + fprintf (state->ofp, " movl (%%rsp), %%%s\n", amd64_dword_reg_name_from_any (ireg)); + } + + } else { + + fprintf (state->ofp, " fstpl (%%rsp)\n"); + fprintf (state->ofp, " movsd (%%rsp), %%%s\n", xmm); + + if (duplicate_gp && ireg) { + fprintf (state->ofp, " movq (%%rsp), %%%s\n", ireg); + } + + } + + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + } else { + + offset = 32 + (index - 4) * 8; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp dword [rsp + %d]\n" : " fstp dword ptr [rsp + %d]\n"), offset); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [rsp + %d]\n" : " fstp qword ptr [rsp + %d]\n"), offset); + } + + } else { + + if (size == (DATA_FLOAT & 0x1f)) { + fprintf (state->ofp, " fstps %d(%%rsp)\n", offset); + } else { + fprintf (state->ofp, " fstpl %d(%%rsp)\n", offset); + } + + } + + } + +} + +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_is_floating, ch, i; + int arg_bytes; + + int saved_arg_assignment32_stop_before_condition_operator; + int saved_arg_assignment64_stop_before_condition_operator; + + 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_saved_ofp = 0; + FILE *arg_tmp_ofp = 0; + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps = 0; + + int *arg_tmp_floating = 0; + int *new_arg_tmp_floating = 0; + int *arg_tmp_float_sizes = 0; + int *new_arg_tmp_float_sizes = 0; + + int arg_saved_stack_mod16 = 0; + int amd64_call_stack_bytes = 0; + int hidden_struct_return_arg = 0; + int effective_argc = 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 * 8; + + if (state->ofp) { + + inline_tmp_ofp = scc_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 rsp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " subq $%d, %%rsp\n", inline_arg_bytes); + } + + } + + } + + } + + saved_arg_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + saved_arg_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; + + assignment32_stop_before_condition_operator = 0; + assignment64_stop_before_condition_operator = 0; + + 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 [rbp + 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 [rsp], argument 1 at + * [rsp + 4], etc., so do not reverse inline argument copies here. + */ + if (!use_inline && state->ofp) { + + arg_tmp_ofp = scc_tmpfile (); + + if (arg_tmp_ofp) { + + arg_saved_stack_mod16 = amd64_temp_stack_mod16; + arg_saved_ofp = state->ofp; + + amd64_temp_stack_mod16 = 0; + 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 () && !find_local_symbol (tok.ident) && 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_rsp_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, "rax", 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 (!use_inline && arg_is_floating && + get_global_symbol_has_prototype (name) && + argc < get_global_symbol_param_count (name) && + get_global_symbol_param_floating (name, argc) && + get_global_symbol_param_size (name, argc) == (DATA_FLOAT & 0x1f)) { + arg_bytes = DATA_FLOAT & 0x1f; + } + + if (!use_inline && get_global_symbol_has_prototype (name) && + argc < get_global_symbol_param_count (name) && + get_global_symbol_param_size (name, argc) == (DATA_LLONG & 0x1f) && + get_global_symbol_param_pointer_depth (name, argc) == 0 && + !get_global_symbol_param_floating (name, argc)) { + + arg_is_floating = 0; + arg_bytes = DATA_LLONG & 0x1f; + + amd64_emit_load_assignment_rhs_expression_to_rax64 (get_global_symbol_param_unsigned (name, argc)); + + } else if (!use_inline && get_global_symbol_has_prototype (name) && get_global_symbol_is_variadic (name) && argc >= get_global_symbol_param_count (name) && current_argument_starts_64bit_integer_now ()) { + + arg_is_floating = 0; + arg_bytes = DATA_LLONG & 0x1f; + + amd64_emit_load_assignment_rhs_expression_to_rax64 (rhs_current_operand_is_unsigned_now ()); + + } else if (arg_is_floating) { + emit_load_floating_rhs_expression_now (arg_bytes); + } else { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + if (state->ofp) { + + if (!use_inline && !arg_is_floating && arg_bytes == (DATA_LLONG & 0x1f)) { + + /** + * AMD64 passes scalar arguments in 64-bit slots. Do + * not emit the old i386 EDX:EAX stack-push sequence + * here; the completed argument evaluation is replayed + * below and assigned to RCX/RDX/R8/R9 or a qword stack + * slot according to the AMD64 calling convention. + */ + + } else 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 ("rax", 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 ("rax", 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 qword [rsp + %d], rax\n" : " mov qword ptr [rsp + %d], rax\n"), argc * 8); + } else { + fprintf (state->ofp, " movq %%rax, %d(%%rsp)\n", argc * 8); + } + + } + + } else if (arg_is_floating) { + /* Keep ST(0) live; amd64_emit_move_st0_to_arg() places it. */ + } else { + /* Keep RAX live; the AMD64 call replay moves it to the proper slot. */ + } + + } + + } + + if (!use_inline) { + total_arg_bytes += arg_bytes; + } + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + + amd64_temp_stack_mod16 = arg_saved_stack_mod16; + state->ofp = arg_saved_ofp; + + new_arg_tmp_ofps = (FILE **) xrealloc (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; + + } + + new_arg_tmp_floating = (int *) xrealloc (arg_tmp_floating, sizeof (*arg_tmp_floating) * (argc + 1)); + + if (new_arg_tmp_floating) { + + arg_tmp_floating = new_arg_tmp_floating; + arg_tmp_floating[argc] = arg_is_floating; + + } + + new_arg_tmp_float_sizes = (int *) xrealloc (arg_tmp_float_sizes, sizeof (*arg_tmp_float_sizes) * (argc + 1)); + + if (new_arg_tmp_float_sizes) { + + arg_tmp_float_sizes = new_arg_tmp_float_sizes; + arg_tmp_float_sizes[argc] = arg_is_floating ? arg_bytes : 0; + + } + + } + + if (arg_tmp_ofp) { + + scc_close (arg_tmp_ofp); + arg_tmp_ofp = 0; + + } + + argc++; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + assignment32_stop_before_condition_operator = saved_arg_assignment32_stop_before_condition_operator; + assignment64_stop_before_condition_operator = saved_arg_assignment64_stop_before_condition_operator; + + 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 rsp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " addq $%d, %%rsp\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 rsp, %d\n", inline_arg_bytes); + } else { + fprintf (state->ofp, " addq $%d, %%rsp\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]) { + scc_close (arg_tmp_ofps[i]); + } + + } + + for (i = hidden_struct_return_arg; i < effective_argc && i < 4; i++) { + + int user_index = i - hidden_struct_return_arg; + + if (user_index >= 0 && user_index < argc && !(arg_tmp_floating && arg_tmp_floating[user_index])) { + amd64_emit_load_home_arg_to_reg (i); + } + + } + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + if (arg_tmp_floating) { + + free (arg_tmp_floating); + arg_tmp_floating = 0; + + } + + if (arg_tmp_float_sizes) { + + free (arg_tmp_float_sizes); + arg_tmp_float_sizes = 0; + + } + + 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) { + + /* + * Microsoft x64 requires the caller to reserve the 32-byte home area + * for every call, even when the call has no arguments. The old code + * only reserved it when arg_tmp_ofps was non-null, so zero-argument + * calls emitted no shadow space at all. + */ + hidden_struct_return_arg = (saved_pending_struct_return_lhs || saved_pending_struct_return_global_name || saved_pending_struct_return_stack_address || saved_pending_struct_return_stack_top) ? 1 : 0; + effective_argc = argc + hidden_struct_return_arg; + + amd64_call_stack_bytes = 32; + + if (effective_argc > 4) { + amd64_call_stack_bytes += (effective_argc - 4) * 8; + } + + amd64_call_stack_bytes = amd64_align_call_stack_bytes (amd64_call_stack_bytes); + amd64_emit_sub_rsp_bytes (amd64_call_stack_bytes); + + if (arg_tmp_ofps) { + + /* + * Replay argument evaluations from right to left. Each captured + * argument fragment may contain calls, and a nested call is free + * to clobber RCX/RDX/R8/R9. Replaying left-to-right can therefore + * load an early argument register, evaluate a later argument that + * calls out, and lose the earlier register value before the final + * call. Assign the low-numbered register arguments last. + */ + for (i = argc - 1; i >= 0; i--) { + + if (arg_tmp_ofps[i]) { + + int arg_index = i + hidden_struct_return_arg; + fseek (arg_tmp_ofps[i], 0, SEEK_SET); + + while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { + fputc (ch, state->ofp); + } + + if (arg_tmp_floating && arg_tmp_floating[i]) { + + amd64_emit_move_st0_to_arg (arg_index, + (arg_tmp_float_sizes && arg_tmp_float_sizes[i]) ? arg_tmp_float_sizes[i] : + ((get_global_symbol_has_prototype (name) && + i < get_global_symbol_param_count (name) && + get_global_symbol_param_floating (name, i) && + get_global_symbol_param_size (name, i) == (DATA_FLOAT & 0x1f)) ? (DATA_FLOAT & 0x1f) : (DATA_DOUBLE & 0x1f)), + (!get_global_symbol_has_prototype (name) || + (get_global_symbol_is_variadic (name) && + i >= get_global_symbol_param_count (name)))); + + } else if (arg_index < 4) { + amd64_emit_store_rax_to_home_arg (arg_index); + } else { + amd64_emit_store_rax_to_stack_arg (arg_index - 4); + } + + scc_close (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + /* + * Non-floating AMD64 register arguments were staged into the + * caller's home area while replaying captured argument code. + * Load RCX/RDX/R8/R9 only after all argument evaluations have + * finished, otherwise a nested call in a later argument can + * clobber an earlier register argument before the final call. + */ + for (i = 0; i < argc; i++) { + + int arg_index = i + hidden_struct_return_arg; + + if (arg_index >= 4) { + continue; + } + + if (arg_tmp_floating && arg_tmp_floating[i]) { + continue; + } + + amd64_emit_load_home_arg_to_reg (arg_index); + + } + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + if (arg_tmp_floating) { + + free (arg_tmp_floating); + arg_tmp_floating = 0; + + } + + if (arg_tmp_float_sizes) { + + free (arg_tmp_float_sizes); + arg_tmp_float_sizes = 0; + + } + + } + + if (hidden_struct_return_arg) { + + 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; + + /* + * The hidden return-buffer pointer occupies argument register 0. + * Emit it after replaying user arguments so nested argument + * evaluation cannot clobber RCX. + */ + emit_load_pending_struct_return_address_to_rax_now (amd64_call_stack_bytes); + amd64_emit_move_rax_to_arg_reg (0); + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (call_sym) { + + emit_load_local_to_reg ("r11", call_sym->offset, DATA_PTR); + fprintf (state->ofp, " call r11\n"); + + } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) { + + emit_load_global_to_reg ("r11", name, DATA_PTR); + fprintf (state->ofp, " call r11\n"); + + } else if (get_global_symbol_dllimport (name)) { + + remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " call qword [%s]\n" : " call qword ptr [%s]\n"), asm_global_import_symbol_name (name)); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (amd64_call_stack_bytes > 0) { + amd64_emit_add_rsp_bytes (amd64_call_stack_bytes); + } + + if (strcmp (reg, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } + + } else { + + if (call_sym) { + + emit_load_local_to_reg ("r11", call_sym->offset, DATA_PTR); + fprintf (state->ofp, " call *%%r11\n"); + + } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) { + + emit_load_global_to_reg ("r11", name, DATA_PTR); + fprintf (state->ofp, " call *%%r11\n"); + + } else if (get_global_symbol_dllimport (name)) { + + remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); + fprintf (state->ofp, " call *%s\n", asm_global_import_symbol_name (name)); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (amd64_call_stack_bytes > 0) { + amd64_emit_add_rsp_bytes (amd64_call_stack_bytes); + } + + if (strcmp (reg, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%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]) { + scc_close (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + +} + +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 = 0; + +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; + + /* + * With a single fixed function frame, block-local symbols still have + * different logical stack depths, but ESP no longer changes when + * entering/leaving those blocks. The old trampoline fixup would + * therefore corrupt ESP before the real jump target. + */ + if (!current_function_uses_single_frame) { + + 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 rax, %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, " cmpq $%ld, %%rax\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 ("rax"); + + 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 = scc_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); + + scc_close (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) { + + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + + return 1; + + default: + + return 0; + + } + +} + +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_rax_rdx_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 rax, rdx\n"); + fprintf (state->ofp, " %s al\n", setcc); + fprintf (state->ofp, " movzx eax, al\n"); + + if (strcmp (reg, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } + + } else { + + fprintf (state->ofp, " cmpq %%rdx, %%rax\n"); + fprintf (state->ofp, " %s %%al\n", setcc); + fprintf (state->ofp, " movzbl %%al, %%eax\n"); + + if (strcmp (reg, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%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) { + + 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, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rax, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rax\n", reg); + } + + } + + emit_push_reg_now ("rax"); + rhs_is_unsigned = emit_load_assignment_binary_expression_prec_to_reg ("rdx", 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 ("rdx", scale_rhs); + } + + emit_pop_reg_now ("rax"); + + if (scale_lhs > 1) { + emit_scale_reg_by_const_now ("rax", 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_rax_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, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%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 (current_integer_expr_is_foldable_now ()) { + + 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_statement_const32_to_rdx (int64_s v); + +static int is_assignment32_condition_stop_operator (enum token_kind k) { + + /* + * Keep this as a switch rather than a chained || expression. This guard + * protects statement-condition parsing from consuming compare/logical + * operators too early; if a self-built stage miscompiles the || chain + * here, ordinary conditions can degrade into: + * + * mov rax, + * test rax, rax + */ + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + case TOK_LOGAND: + case TOK_LOGOR: + + return 1; + + default: + + return 0; + + } + +} + +static void emit_load_assignment_compare_expression_to_reg (const char *reg) { + + int64_s rhs_enum_value; + enum token_kind op; + + int lhs_pointer_depth; + int is_unsigned; + int rhs_is_enum; + + 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 (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { + return; + } + + if (is_value_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + rhs_is_enum = 0; + + if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &rhs_enum_value)) { + + rhs_is_enum = 1; + get_token (); + + } + + if (strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rax, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rax\n", reg); + } + + } + + /* + * Keep identifier-vs-enum comparisons structurally as a real compare. + * During bootstrap this path is hit when compiling ordinary tests such + * as: + * + * unary_op == TOK_MINUS + * + * If the RHS enum is allowed to go through the generic expression loader, + * a later self-built compiler can collapse the expression into just: + * + * mov rax, 45 + * test rax, rax + * + * which makes every non-zero enum comparison true. Loading the enum + * directly into EDX avoids clobbering the already-loaded LHS in EAX and + * avoids the fragile push/pop/generic-RHS sequence entirely. + */ + if (rhs_is_enum) { + + emit_statement_const32_to_rdx (rhs_enum_value); + + if (lhs_pointer_depth > 0) { + is_unsigned = 1; + } + + emit_compare_rax_rdx_to_reg (op, reg, is_unsigned); + return; + + } + + emit_push_reg_now ("rax"); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + emit_load_assignment_binary_expression_to_reg ("rdx"); + } + + if (lhs_pointer_depth > 0 || rhs_last_pointer_depth > 0) { + is_unsigned = 1; + } + + emit_pop_reg_now ("rax"); + emit_compare_rax_rdx_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, " testq %%%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, " testq %%%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) { + + const char *dreg = amd64_dword_reg_name_from_any (reg); + + if (!state->ofp || !dreg) { + return; + } + + /* + * This helper is used by boolean/integer-result paths. Load through the + * dword register so AMD64 clears the upper half instead of relying on a + * qword immediate move with sign-extension-sensitive encodings. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, %ld\n", dreg, value); + } else { + fprintf (state->ofp, " movl $%ld, %%%s\n", value, dreg); + } + +} + +static void emit_floating_stack_to_int_reg_now (const char *reg) { + + if (!state->ofp) { + return; + } + + /* + * C requires floating-to-integer conversion to discard the fractional + * part. x87 fistp uses the current FPU rounding mode, which is normally + * round-to-nearest, so values such as 0.5000000001 become 1 instead of 0. + * Temporarily switch the x87 control word to truncate for this conversion. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [rsp + 4]\n" : " fnstcw word ptr [rsp + 4]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [rsp + 4]\n" : " mov ax, word ptr [rsp + 4]\n"); + fprintf (state->ofp, " or ah, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [rsp + 6], ax\n" : " mov word ptr [rsp + 6], ax\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [rsp + 6]\n" : " fldcw word ptr [rsp + 6]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp dword [rsp]\n" : " fistp dword ptr [rsp]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [rsp + 4]\n" : " fldcw word ptr [rsp + 4]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp]\n" : " mov %s, dword ptr [rsp]\n", amd64_dword_reg_name_from_any (reg)); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " fnstcw 4(%%rsp)\n"); + fprintf (state->ofp, " movw 4(%%rsp), %%ax\n"); + fprintf (state->ofp, " orb $12, %%ah\n"); + fprintf (state->ofp, " movw %%ax, 6(%%rsp)\n"); + fprintf (state->ofp, " fldcw 6(%%rsp)\n"); + fprintf (state->ofp, " fistpl (%%rsp)\n"); + fprintf (state->ofp, " fldcw 4(%%rsp)\n"); + fprintf (state->ofp, " movl (%%rsp), %%%s\n", amd64_dword_reg_name_from_any (reg)); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + +} + +static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [rsp + 8]\n" : " fnstcw word ptr [rsp + 8]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [rsp + 8]\n" : " mov ax, word ptr [rsp + 8]\n"); + fprintf (state->ofp, " or ah, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [rsp + 10], ax\n" : " mov word ptr [rsp + 10], ax\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [rsp + 10]\n" : " fldcw word ptr [rsp + 10]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp qword [rsp]\n" : " fistp qword ptr [rsp]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [rsp + 8]\n" : " fldcw word ptr [rsp + 8]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp]\n" : " mov %s, dword ptr [rsp]\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [rsp + 4]\n" : " mov %s, dword ptr [rsp + 4]\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " add rsp, 12\n"); + + } else { + + fprintf (state->ofp, " subq $12, %%rsp\n"); + fprintf (state->ofp, " fnstcw 8(%%rsp)\n"); + fprintf (state->ofp, " movw 8(%%rsp), %%ax\n"); + fprintf (state->ofp, " orb $12, %%ah\n"); + fprintf (state->ofp, " movw %%ax, 10(%%rsp)\n"); + fprintf (state->ofp, " fldcw 10(%%rsp)\n"); + fprintf (state->ofp, " fistpll (%%rsp)\n"); + fprintf (state->ofp, " fldcw 8(%%rsp)\n"); + fprintf (state->ofp, " movl (%%rsp), %%%s\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " movl 4(%%rsp), %%%s\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " addq $12, %%rsp\n"); + + } + +} + +static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size) { + + if (!state->ofp) { + return; + } + + if ((size & 0x1f) == (DATA_LLONG & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [rsp], %s\n" : " mov dword ptr [rsp], %s\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [rsp + 4], %s\n" : " mov dword ptr [rsp + 4], %s\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild qword [rsp]\n" : " fild qword ptr [rsp]\n"); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%%s, (%%rsp)\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " movl %%%s, 4(%%rsp)\n", amd64_dword_reg_name_from_any (hi)); + fprintf (state->ofp, " fildll (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub rsp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [rsp], %s\n" : " mov dword ptr [rsp], %s\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild dword [rsp]\n" : " fild dword ptr [rsp]\n"); + fprintf (state->ofp, " add rsp, 8\n"); + + } else { + + fprintf (state->ofp, " subq $8, %%rsp\n"); + fprintf (state->ofp, " movl %%%s, (%%rsp)\n", amd64_dword_reg_name_from_any (lo)); + fprintf (state->ofp, " fildl (%%rsp)\n"); + fprintf (state->ofp, " addq $8, %%rsp\n"); + + } + +} + +static void emit_load_assignment_rhs_expression_to_reg (const char *reg) { + + int false_label; + int true_label; + int end_label; + + if (rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + + /* + * Floating comparisons leave their boolean result in rax. + * Do not run that through fistp: emit_floating_compare_to_rax_now() + * has already consumed both x87 operands with fcompp, so the x87 + * stack no longer contains a value to convert. + */ + if (floating_rhs_result_in_eax_bool) { + + if (state->ofp && strcmp (reg, "rax") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%s\n", reg); + } + + } + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + emit_floating_stack_to_int_reg_now (reg); + return; + + } + + emit_load_assignment_compare_expression_to_reg (reg); + + for (;;) { + + if (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { + break; + } + + if (tok.kind == TOK_LOGAND) { + + false_label = anon_label++; + true_label = anon_label++; + end_label = anon_label++; + + get_token (); + + emit_test_reg_jump_zero_now (reg, false_label); + emit_load_assignment_compare_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); + + continue; + + } + + if (tok.kind == TOK_LOGOR) { + + false_label = anon_label++; + true_label = anon_label++; + end_label = anon_label++; + + get_token (); + + emit_test_reg_jump_nonzero_now (reg, true_label); + emit_load_assignment_compare_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); + + continue; + + } + + break; + + } + + if (tok.kind == TOK_QMARK) { + + false_label = anon_label++; + end_label = anon_label++; + + get_token (); + + if (state->ofp) { + + if (strcmp (reg, "rax") != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rax, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rax\n", reg); + } + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test rax, rax\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, " testq %%rax, %%rax\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_rax (enum token_kind op, int is_unsigned); + +static int is_assignment64_condition_stop_operator (enum token_kind k) { + + return 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 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, "rax") != 0) { + fprintf (state->ofp, " mov %s, rax\n", lo); + } + + fprintf (state->ofp, " xor %s, %s\n", hi, hi); + + } else { + + if (strcmp (lo, "rax") != 0) { + fprintf (state->ofp, " movq %%rax, %%%s\n", lo); + } + + fprintf (state->ofp, " xorq %%%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 rdx, rdx\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 rax, rax\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 rcx, rcx\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 rbx, rbx\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 rax, rax\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 rcx, rcx\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 rbx, rbx\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 rax, rax\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, " testq %%rdx, %%rdx\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, " testq %%rax, %%rax\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, " testq %%rcx, %%rcx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testq %%rbx, %%rbx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xorq %%rax, %%rax\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, " testq %%rcx, %%rcx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testq %%rbx, %%rbx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " xorq %%rax, %%rax\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_rax (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_rax_rdx = (strcmp (lo, "rax") == 0 && strcmp (hi, "rdx") == 0); + int current_pair_is_rax_rdx = result_pair_is_rax_rdx; + + if (current_integer_expr_is_foldable_now ()) { + + 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) && !(assignment64_stop_before_condition_operator && is_assignment64_condition_stop_operator (tok.kind))) { + + op = tok.kind; + get_token (); + + if (!current_pair_is_rax_rdx) { + + emit_mov_reg_to_reg_now ("rax", lo); + emit_mov_reg_to_reg_now ("rdx", hi); + + current_pair_is_rax_rdx = 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 ("rax"); + emit_push_reg_now ("rdx"); + + emit_load_assignment_binary_expression_to_reg ("rbx"); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " xor rcx, rcx\n"); + } else { + fprintf (state->ofp, " xorq %%rcx, %%rcx\n"); + } + + emit_pop_reg_now ("rdx"); + emit_pop_reg_now ("rax"); + + } 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 ("rax"); + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_to_pair ("rbx", "rcx"); + + emit_pop_reg_now ("rdx"); + emit_pop_reg_now ("rax"); + + } + + if (op == TOK_LOGAND || op == TOK_LOGOR) { + + emit_assignment64_logical_op_to_pair_now (op, lo, hi); + current_pair_is_rax_rdx = result_pair_is_rax_rdx; + + } else if (is_value_compare_operator (op)) { + + emit_assignment64_compare_op_to_pair_now (op, lo, hi, is_unsigned); + current_pair_is_rax_rdx = result_pair_is_rax_rdx; + + } else { + + emit_assignment_binary_op64 (op, is_unsigned); + current_pair_is_rax_rdx = 1; + + } + + emit_restore_assignment64_regs (op); + + } + + if (current_pair_is_rax_rdx && !result_pair_is_rax_rdx) { + + emit_mov_reg_to_reg_now (lo, "rax"); + emit_mov_reg_to_reg_now (hi, "rdx"); + + current_pair_is_rax_rdx = 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, " testq %%%s, %%%s\n", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", false_label + 2); + fprintf (state->ofp, " testq %%%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; + + int qword; + + if (!state->ofp) { + return; + } + + size &= 0x1f; + qword = (size == (DATA_LLONG & 0x1f) || size == (DATA_PTR & 0x1f)); + + if (size != (DATA_CHAR & 0x1f) && + size != (DATA_SHORT & 0x1f) && + size != (DATA_INT & 0x1f) && + size != (DATA_LONG & 0x1f) && + size != (DATA_LLONG & 0x1f) && + size != (DATA_PTR & 0x1f)) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + mnemonic = op == TOK_INCR ? "add" : "sub"; + + if (sym && !sym->is_static) { + + format_intel_rbp_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 if (qword) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s qword %s, 1\n" : " %s qword 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(%%rbp)\n" : " decb %ld(%%rbp)\n", sym->offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, op == TOK_INCR ? " incw %ld(%%rbp)\n" : " decw %ld(%%rbp)\n", sym->offset); + } else if (qword) { + fprintf (state->ofp, op == TOK_INCR ? " incq %ld(%%rbp)\n" : " decq %ld(%%rbp)\n", sym->offset); + } else { + fprintf (state->ofp, op == TOK_INCR ? " incl %ld(%%rbp)\n" : " decl %ld(%%rbp)\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 if (qword) { + fprintf (state->ofp, op == TOK_INCR ? " incq %s\n" : " decq %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_rbp_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 qword %s, %d\n", mnemonic, symbol, step); + + } else { + fprintf (state->ofp, " %s qword ptr %s, %d\n", mnemonic, symbol, step); + } + + } else { + + if (sym && !sym->is_static) { + fprintf (state->ofp, " %sq $%d, %ld(%%rbp)\n", mnemonic, step, sym->offset); + } else { + + symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name); + fprintf (state->ofp, " %sq $%d, %s(%%rip)\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 (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + if (emit_load_prefix_incdec_member_to_reg_now ("rax")) { + + expect_semi_or_recover (); + return 1; + + } + + } + + if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now ("rax")) { + + 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 ("rdx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", 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 ("rdx", 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 ("rdx"); + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, op == TOK_INCR ? " add rax, 1\n" : " sub rax, 1\n"); + } else { + fprintf (state->ofp, op == TOK_INCR ? " addq $1, %%rax\n" : " subq $1, %%rax\n"); + } + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rcx", prefix_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", prefix_sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", prefix_name, DATA_PTR); + } + + emit_push_reg_now ("rcx"); + + if (prefix_assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_pop_reg_now ("rdx"); + emit_push_reg_now ("rdx"); + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, prefix_deref_size); + emit_push_reg_now ("rax"); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + emit_assignment_binary_op (prefix_assign_op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rcx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", sym->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", name, DATA_PTR); + } + + emit_push_reg_now ("rcx"); + + 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 qword [%s], %d\n" : " %s qword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", asm_global_symbol_name (sym->static_label), step); + } else { + fprintf (state->ofp, " %sq $%d, %s(%%rip)\n", postfix_op == TOK_INCR ? "add" : "sub", step, asm_global_symbol_name (sym->static_label)); + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + char memref[64]; + char nasm_memref[128]; + const char *out_memref; + + format_intel_rbp_offset (memref, sizeof (memref), sym->offset); + out_memref = memref; + + if (state->syntax & ASM_SYNTAX_NASM) { + out_memref = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), memref); + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s qword %s, %d\n" : " %s qword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", out_memref, step); + + } else { + fprintf (state->ofp, " %sq $%d, %ld(%%rbp)\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 qword [%s], %d\n" : " %s qword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", asm_global_symbol_name (name), step); + } else { + fprintf (state->ofp, " %sq $%d, %s(%%rip)\n", postfix_op == TOK_INCR ? "add" : "sub", step, asm_global_symbol_name (name)); + } + + } + + } + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_pop_reg_now ("rdx"); + emit_push_reg_now ("rdx"); + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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, "rax", 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 ("rax", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg ("rax", 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 ("rax", DATA_PTR); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rdx, rax\n"); + } else { + fprintf (state->ofp, " movq %%rax, %%rdx\n"); + } + + } else if (tok.kind == TOK_LPAREN) { + emit_call_identifier_to_reg_now (name, "rax", 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 ("rdx", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", 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 ("rdx", 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 ("rdx", deref_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s rdx, %d\n", postfix_op == TOK_INCR ? "add" : "sub", step); + } else { + fprintf (state->ofp, " %sq $%d, %%rdx\n", postfix_op == TOK_INCR ? "add" : "sub", step); + } + + if (sym) { + + if (sym->is_static && sym->static_label) { + emit_load_global_to_reg ("rax", sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", sym->offset, DATA_PTR); + } + + } else if (global_index >= 0) { + emit_load_global_to_reg ("rax", name, DATA_PTR); + } + + emit_store_reg_to_deref_reg_now ("rax", "rdx", 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 ("rcx", "rdx", 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 qword [rdx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } else { + fprintf (state->ofp, " %s qword ptr [rdx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } + + } else { + fprintf (state->ofp, " %sq $%d, %d(%%rdx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset); + } + + } + + emit_push_reg_now ("rcx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_first_array_count = declarator_first_array_count; + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + 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 ("rdx", addr_sym->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", addr_sym->offset, DATA_PTR); + } + + } else if (addr_global_index >= 0) { + emit_load_global_to_reg ("rdx", 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 ("rdx"); + } + + while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) { + + enum token_kind addr_op = tok.kind; + get_token (); + + emit_push_reg_now ("rdx"); + emit_load_assignment_rhs_to_reg ("rax"); + + if (deref_size > 1) { + emit_scale_reg_by_const_now ("rax", deref_size); + } + + emit_pop_reg_now ("rdx"); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s rdx, rax\n", addr_op == TOK_PLUS ? "add" : "sub"); + } else { + fprintf (state->ofp, " %sq %%rax, %%rdx\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 ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_push_reg_now ("rdx"); + emit_load_deref_reg_now ("rax", deref_size); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", deref_size); + expect_semi_or_recover (); + + return 1; + +} + +static int lparen_expression_starts_with_star_now (void) { + + const char *p; + + if (tok.kind != TOK_LPAREN || !tok.caret) { + return 0; + } + + p = tok.caret + 1; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '*'; + +} + +static int parse_parenthesized_indirect_assignment_statement (void) { + + enum token_kind op; + + int pointer_depth; + int pointed_size; + int deref_size = DATA_INT & 0x1f; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + pointer_depth = rhs_last_pointer_depth; + pointed_size = rhs_last_pointed_size; + + if (pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (pointer_depth == 1 && pointed_size > 0) { + deref_size = pointed_size & 0x1f; + } + + if (deref_size <= 0) { + deref_size = DATA_INT & 0x1f; + } + + expect (TOK_RPAREN, ")"); + + if (!is_assignment_operator (tok.kind)) { + + expect_semi_or_recover (); + return 1; + + } + + op = tok.kind; + get_token (); + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_push_reg_now ("rdx"); + + emit_load_deref_reg_now ("rax", deref_size); + emit_push_reg_now ("rax"); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 deref_size = DATA_INT & 0x1f; + int deref_is_floating = 0; + + enum token_kind op; + int global_index; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now ("rax")) { + + 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 ("rcx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", name, DATA_PTR); + } + + emit_load_deref_reg_now ("rcx", DATA_PTR & 0x1f); + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("rcx"); + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rcx"); + + } else { + + emit_push_reg_now ("rcx"); + + emit_load_member_from_addr_reg_now ("rax", "rcx", 0, deref_size); + emit_push_reg_now ("rax"); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rcx"); + + } + + emit_store_reg_to_deref_reg_now ("rcx", "rax", 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 (); + } + + if (lparen_expression_starts_with_star_now ()) { + return parse_parenthesized_pointer_member_indirect_assignment_statement (); + } + + return parse_parenthesized_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 ("rcx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rcx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rcx", name, DATA_PTR); + } + + emit_push_reg_now ("rcx"); + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s qword [%s], %d\n" : " %s qword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", asm_global_symbol_name (lhs->static_label), step); + } else { + fprintf (state->ofp, " %sq $%d, %s(%%rip)\n", postfix_op == TOK_INCR ? "add" : "sub", step, asm_global_symbol_name (lhs->static_label)); + } + + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + char memref[64]; + char nasm_memref[128]; + const char *out_memref; + + format_intel_rbp_offset (memref, sizeof (memref), lhs->offset); + out_memref = memref; + + if (state->syntax & ASM_SYNTAX_NASM) { + out_memref = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), memref); + } + + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s qword %s, %d\n" : " %s qword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", out_memref, step); + + } else { + fprintf (state->ofp, " %sq $%d, %ld(%%rbp)\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 qword [%s], %d\n" : " %s qword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", asm_global_symbol_name (name), step); + } else { + fprintf (state->ofp, " %sq $%d, %s(%%rip)\n", postfix_op == TOK_INCR ? "add" : "sub", step, asm_global_symbol_name (name)); + } + + } + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_pop_reg_now ("rdx"); + emit_push_reg_now ("rdx"); + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("rdx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("rdx", lhs->offset); + } + + } + + } else { + + if (member_op == TOK_ARROW) { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } else { + emit_load_address_to_reg_now ("rdx", name); + } + + } + + emit_load_member_from_addr_reg_now ("rcx", "rdx", 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 qword [rdx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } else { + fprintf (state->ofp, " %s qword ptr [rdx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step); + } + + } else { + fprintf (state->ofp, " %sq $%d, %d(%%rdx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset); + } + + } + + emit_push_reg_now ("rcx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("rdx"); + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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; + deref_is_floating = 0; + + } 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; + deref_is_floating = lhs->pointed_is_floating; + + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 1) { + + deref_size = DATA_PTR & 0x1f; + deref_is_floating = 0; + + } else if (get_global_symbol_pointer_depth (name) == 1) { + + deref_size = get_global_symbol_pointed_size (name); + deref_is_floating = get_global_symbol_pointed_is_floating (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 ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } + + if (op == TOK_ASSIGN) { + + if (deref_is_floating) { + + emit_push_reg_now ("rdx"); + + emit_load_floating_rhs_expression_now (deref_size); + emit_pop_reg_now ("rdx"); + + emit_store_floating_member_to_addr_reg_now ("rdx", 0, deref_size); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + if (deref_size != (DATA_LLONG & 0x1f) && emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", 0, deref_size)) { + + expect_semi_or_recover (); + + free (name); + return 1; + + } + + emit_push_reg_now ("rdx"); + + if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", 1); + emit_pop_reg_now ("rcx"); + + } else { + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + if (deref_size == (DATA_LLONG & 0x1f) && !deref_is_floating) { + emit_store_pair_to_deref_reg_now ("rcx", "rax", "rdx"); + } else { + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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, "rdx", 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 ("rdx", inner_lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", inner_lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rdx", 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 ("rdx"); + } + + } else { + emit_load_assignment_rhs_expression_to_reg ("rdx"); + } + + 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 ("rax", "rdx", 0, deref_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s rax, %d\n", op == TOK_INCR ? "add" : "sub", step); + } else { + fprintf (state->ofp, " %sq $%d, %%rax\n", op == TOK_INCR ? "add" : "sub", step); + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, deref_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rdx", 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 ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", member_offset, member_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_member_to_addr_reg_now ("rdx", member_offset, "rax", 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 ("rax", src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rax", 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 ("rax", 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 ("rax", DATA_PTR & 0x1f); + emit_handle_subscript_after_loaded_pointer_to_reg_now ("rax", pointer_depth - 1, pointed_size, src ? src->pointed_is_unsigned : get_global_symbol_pointed_is_unsigned (name)); + + 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 is_simple_assign; + int lhs_size; + int lhs_is_floating; + int lhs_pointer_depth; + + 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; + int member_assignment_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 ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("rdx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("rdx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("rdx", 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 [rdx + %d], %d\n", opname, opsize, member_offset, step); + } else { + fprintf (state->ofp, " %s %s ptr [rdx + %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(%%rdx)\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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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 ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("rdx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("rdx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("rdx", name); + } + + } + + if (is_simple_assign) { + + member_assignment_is_floating = member_is_floating || rhs_current_operand_is_floating_now (); + emit_push_reg_now ("rdx"); + + if (member_assignment_is_floating) { + emit_load_floating_rhs_expression_now (member_size); + } else { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", member_offset, member_size); + + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + + 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 ("rdx", rhs_const); + + } else if (current_integer_expr_is_foldable_now ()) { + + int64_s rhs_const = const64_from_current_foldable_expr (); + emit_load_const32_to_reg_now ("rdx", rhs_const); + + } else { + emit_load_assignment_rhs_expression_to_reg ("rdx"); + } + + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + if (member_assignment_is_floating && is_simple_assign) { + emit_store_floating_member_to_addr_reg_now ("rdx", member_offset, member_size); + } else { + emit_store_member_to_addr_reg_now ("rdx", member_offset, "rax", 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 ("rdx", lhs->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now ("rdx", 0, lhs->offset, 1); + } + + } else if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else if (get_global_symbol_array (name)) { + emit_load_symbol_address_to_reg_now ("rdx", name, 0, 0); + } else { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now ("rdx", elem_size); + + } else { + emit_parse_postfix_subscript_scaled_address_to_reg_now ("rdx", elem_size); + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + op = tok.kind; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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 ("rax", "rdx", 0, elem_size); + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " %s rax, %d\n", op == TOK_INCR ? "add" : "sub", inc_amount); + } else { + fprintf (state->ofp, " %sq $%d, %%rax\n", op == TOK_INCR ? "add" : "sub", inc_amount); + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + get_token (); + + if (state->ofp) { + + if (is_simple_assign) { + + emit_push_reg_now ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, elem_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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, "rax", 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 rdx, rax\n"); + } else { + fprintf (state->ofp, " movq %%rax, %%rdx\n"); + } + + } + + emit_parse_postfix_subscript_scaled_address_to_reg_now ("rdx", elem_size); + + if (is_assignment_operator (tok.kind)) { + + op = tok.kind; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + get_token (); + + if (state->ofp) { + + if (is_simple_assign) { + + emit_push_reg_now ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, elem_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 ("rax", DATA_PTR); + } + + if (is_assignment_operator (tok.kind)) { + + op = tok.kind; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + get_token (); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rdx, rax\n"); + } else { + fprintf (state->ofp, " movq %%rax, %%rdx\n"); + } + + if (is_simple_assign) { + + emit_push_reg_now ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", member_offset, member_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_member_to_addr_reg_now ("rdx", member_offset, "rax", 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 ("rcx", "rax", member_offset, DATA_PTR & 0x1f); + emit_push_reg_now ("rcx"); + + } + + get_token (); + + if (tok.kind != TOK_RPAREN) { + + for (;;) { + + arg_tmp_ofp = 0; + arg_saved_ofp = 0; + + if (state->ofp) { + + arg_tmp_ofp = scc_tmpfile (); + + if (arg_tmp_ofp) { + arg_saved_ofp = state->ofp; + state->ofp = arg_tmp_ofp; + } + + } + + emit_load_assignment_rhs_expression_to_reg ("rax"); + + if (state->ofp) { + emit_push_reg_now ("rax"); + } + + if (arg_saved_ofp) { + + fflush (arg_tmp_ofp); + state->ofp = arg_saved_ofp; + + new_arg_tmp_ofps = (FILE **) xrealloc (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) { + scc_close (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); + } + + scc_close (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 r11, qword [rsp + %d]\n", total_arg_bytes); + } else { + fprintf (state->ofp, " mov r11, qword ptr [rsp + %d]\n", total_arg_bytes); + } + + fprintf (state->ofp, " call r11\n"); + fprintf (state->ofp, " add rsp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f)); + + } else { + + fprintf (state->ofp, " movq %d(%%rsp), %%r11\n", total_arg_bytes); + fprintf (state->ofp, " call *%%r11\n"); + + fprintf (state->ofp, " addq $%d, %%rsp\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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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); + lhs_pointer_depth = lhs ? lhs->pointer_depth : get_global_symbol_pointer_depth (name); + + if (is_simple_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, "rax", 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 ("rax", rhs_sym, rhs_name, name_start, name_caret, name_line)) { + + emit_load_symbol_address_for_copy_now ("rdx", 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 (is_simple_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) && lhs_pointer_depth <= 0) { + + if (is_simple_assign) { + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("rax", "rdx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "rax", "rdx"); + } + + } else { + emit_load_global64_to_pair ("rax", "rdx", 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 rax:rdx, + * copy it to the RHS pair rbx:rcx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("rax"); + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + emit_mov_reg_to_reg_now ("rbx", "rax"); + emit_mov_reg_to_reg_now ("rcx", "rdx"); + + emit_pop_reg_now ("rdx"); + emit_pop_reg_now ("rax"); + + 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, "rax", "rdx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "rax", "rdx"); + } + + } else { + emit_store_pair_to_global64 (name, "rax", "rdx"); + } + + } else { + + if (is_simple_assign) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg_ex ("rax", lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex ("rax", lhs->offset, lhs->size, lhs->is_unsigned); + } + + } else { + emit_load_global_to_reg ("rax", 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 rax while the RHS expression is parsed, because + * RHS binary-expression code may use rax internally even + * when the requested result register is rdx. + */ + emit_push_reg_now ("rax"); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + emit_scale_reg_for_pointer_compound_assignment_now ("rdx", lhs, name, op); + emit_pop_reg_now ("rax"); + + 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, "rax"); + } else { + emit_store_reg_to_local (lhs->offset, lhs->size, "rax"); + } + + } else { + emit_store_reg_to_global (name, lhs_size, "rax"); + } + + } + + } 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_CCHAR || k == TOK_LCHAR || k == TOK_CINT || k == TOK_CUINT || k == TOK_CLONG || k == TOK_CULONG || k == TOK_CLLONG || k == TOK_CULLONG; +} + +static int token_is_integer_unsigned_constant_now (enum token_kind k) { + return k == TOK_CUINT || k == TOK_CULONG || k == TOK_CULLONG; +} + +#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 "rax"; + } + + if (strchr (constraint, 'b')) { + return "rbx"; + } + + if (strchr (constraint, 'c')) { + return "rcx"; + } + + if (strchr (constraint, 'd')) { + return "rdx"; + } + + if (strchr (constraint, 'S')) { + return "rsi"; + } + + if (strchr (constraint, 'D')) { + return "rdi"; + } + + return 0; + +} + +static const char *inline_asm_reg_for_template (const char *reg32) { + + if (!reg32) { + return ""; + } + + if (strcmp (reg32, "rax") == 0) { + return "al"; + } + + if (strcmp (reg32, "rbx") == 0) { + return "bl"; + } + + if (strcmp (reg32, "rcx") == 0) { + return "cl"; + } + + if (strcmp (reg32, "rdx") == 0) { + return "dx"; + } + + if (strcmp (reg32, "rsi") == 0) { + return "rsi"; + } + + if (strcmp (reg32, "rdi") == 0) { + return "rdi"; + } + + return reg32; + +} + +static int inline_asm_reg_needs_restore (const char *reg32) { + return reg32 && (strcmp (reg32, "rbx") == 0 || strcmp (reg32, "rsi") == 0 || strcmp (reg32, "rdi") == 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, "rax", 3) == 0) || + (len == 3 && strncmp (s, "rbx", 3) == 0) || + (len == 3 && strncmp (s, "rcx", 3) == 0) || + (len == 3 && strncmp (s, "rdx", 3) == 0) || + (len == 3 && strncmp (s, "rsi", 3) == 0) || + (len == 3 && strncmp (s, "rdi", 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_rbp_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(%%rbp)", 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 ("rdx"); + 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 void emit_statement_const32_to_rdx (int64_s v) { + + flush_pending_statement_labels (); + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, %lu\n", v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%edx\n", v.low & U32_MASK); + } + +} + +static int emit_statement_rhs_const32_to_rdx_if_possible (void) { + + int64_s v; + + if (token_is_const_condition_operand_now ()) { + + if (current_integer_expr_is_foldable_now ()) { + v = const64_from_current_foldable_expr (); + } else { + v = const64_from_current_operand (); + } + + emit_statement_const32_to_rdx (v); + return 1; + + } + + if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &v)) { + + get_token (); + + emit_statement_const32_to_rdx (v); + return 1; + + } + + return 0; + +} + +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; + } + + /* + * Parenthrsized floating conditions such as: + * + * if (num < 0) + * while (b >= 10.0) + * + * arrive here while TOK_LPAREN is still current. If we don't recognise + * the name inside the parens as floating, the generic parenthesized + * integer path consumes the expression and compares only the low dword. + */ + if (find_local_symbol (name)) { + + struct local_symbol *src = find_local_symbol (name); + return src && src->is_floating ? 1 : 0; + + } + + if (find_global_symbol (name) >= 0) { + return get_global_symbol_floating (name) ? 1 : 0; + } + + return 0; + +} + +static int source_va_arg_type_is_double_now (const char *p) { + + int paren; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + paren = 0; + + while (*p) { + + if (*p == '(') { + paren++; + } else if (*p == ')') { + + paren--; + + if (paren == 0) { + return 0; + } + + } else if (paren == 1 && *p == ',') { + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (strncmp (p, "double", 6) == 0 && + !(p[6] == '_' || (p[6] >= '0' && p[6] <= '9') || + (p[6] >= 'A' && p[6] <= 'Z') || + (p[6] >= 'a' && p[6] <= 'z'))) { + return 1; + } + + } + + p++; + + } + + return 0; + +} + +static int rhs_current_operand_is_floating_now (void) { + + if (token_is_floating_constant_now ()) { + return 1; + } + + if (tok.kind == TOK_SCC_BUILTIN_VA_ARG) { + + const char *p = tok.caret ? tok.caret + tok.len : 0; + + if (source_va_arg_type_is_double_now (p)) { + 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 (strcmp (tok.ident, "__scc_builtin_va_arg") == 0 && source_va_arg_type_is_double_now (p)) { + return 1; + } + + if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) { + return get_global_function_returns_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_function_returns_floating (tok.ident) ? 1 : 0; + } + + } + + return 0; + +} + +static int64_s floating_ld_to_bits_now (int size, 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, 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) { + + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + + return 1; + + default: + + return 0; + + } + +} + +static void emit_statement_cmp_rax_rdx_jump_if_false (enum token_kind op, int is_unsigned, int label); +static int statement_condition_emit_logical_tail (int label); + +static void emit_statement_jump_if_false (int label); +static int emit_statement_cmp_rax_rdx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label); + +static int source_condition_tail_end_now (const char *p) { + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + switch (*p) { + + case '\0': + case ')': + case ';': + case '{': + + return 1; + + default: + + break; + + } + + return 0; + +} + +static int source_condition_skip_rhs_const_or_enum_now (const char **pp) { + + char word[128]; + + const char *p; + int i; + + int64_s ignored; + + if (!pp || !*pp) { + return 0; + } + + p = *pp; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + i = 0; + + while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || + (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && + i + 1 < (int) sizeof (word)) { + i++; + } + + memcpy (word, p, (unsigned long) i); + word[i] = 0; + + if (!resolve_enum_constant (word, &ignored)) { + return 0; + } + + p += i; + + *pp = p; + return 1; + + } + + if (*p == '\'') { + + p++; + + while (*p && *p != '\'') { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p != '\'') { + return 0; + } + + p++; + + *pp = p; + return 1; + + } + + if (*p == '-' || *p == '+') { + p++; + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'F') || + (*p >= 'a' && *p <= 'f') || + *p == 'x' || *p == 'X' || + *p == 'u' || *p == 'U' || + *p == 'l' || *p == 'L') { + p++; + } + + *pp = p; + return 1; + +} + +static int emit_statement_rhs_const32_or_enum_to_rdx_if_possible (void) { + + int64_s v; + + if (tok.kind == TOK_IDENT && tok.ident && resolve_enum_constant (tok.ident, &v)) { + + get_token (); + + emit_statement_const32_to_rdx (v); + return 1; + + } + + if (token_is_const_condition_operand_now ()) { + + if (current_integer_expr_is_foldable_now ()) { + v = const64_from_current_foldable_expr (); + } else { + v = const64_from_current_operand (); + } + + emit_statement_const32_to_rdx (v); + return 1; + + } + + return 0; + +} + +static int source_condition_logical_rhs_is_enum_compare_now (const char *p) { + + if (!p) { + return 0; + } + + for (;;) { + + if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) { + p += 2; + } else { + return 0; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(') { + return 0; + } + + for (;;) { + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + break; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (source_condition_tail_end_now (p)) { + return 1; + } + + if (!((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|'))) { + return 0; + } + + } + +} + +static const char *source_find_current_ident_on_line (const char *p) { + + int name_len; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + name_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) name_len) == 0 && + !ident_char_now ((unsigned char) p[name_len])) { + return p + name_len; + } + + p++; + + } + + return 0; + +} + +static int source_condition_ident_enum_compare_now (const char *p) { + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + /* + * tok.start is usually the beginning of the source line, not the current + * identifier. When compiling the compiler itself this made simple enum + * tests such as: + * + * if (assign_op == TOK_ASSIGN) + * + * miss the fixed compare-emission path and fall into the generic + * expression folder, where later bootstrap stages reduced the condition + * to "mov rax, TOK_ASSIGN; test rax, rax". Locate the current token on + * the line first, then inspect the text that follows it. + */ + p = source_find_current_ident_on_line (p); + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(' || *p == '.' || (p[0] == '-' && p[1] == '>')) { + return 0; + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + /* + * This fast path emits a branch-form compare, which is exactly what is + * needed for logical tails. Do not send enum-token compares through the + * generic expression path: later self-built stages have repeatedly reduced + * them to "mov rax, ; test rax,rax". + */ + if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { + return source_condition_logical_rhs_is_enum_compare_now (p); + } + + return source_condition_tail_end_now (p); + +} + +static int source_condition_ident_immediate_compare_now (const char *p) { + + int name_len; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + name_len = (int) strlen (tok.ident); + p = source_find_current_ident_on_line (p); + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + /* + * Only catch immediate primary/member comparisons here. Do not claim + * calls, subscripts, or arithmetic expressions; those still need the + * ordinary expression parser. + */ + if (*p == '(' || *p == '[') { + return 0; + } + + for (;;) { + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + break; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + p++; + } else { + + (void) name_len; + return 0; + + } + + /* + * This shortcut emits one cmp/jcc and returns to the statement parser. + * It must only claim a whole simple condition. If the comparison is + * followed by a top-level logical operator, for example: + * + * m->nargs >= 0 || m->is_variadic + * ch >= 'a' && ch <= 'f' + * + * then claiming only the first comparison leaves the ||/&& tail to a + * parser path that expects an integer constant expression. Let the + * ordinary expression parser own those full logical conditions instead. + */ + { + + int paren_depth = 0; + int bracket_depth = 0; + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + continue; + + } + + if (*p == '(') { + + paren_depth++; + p++; + + continue; + + } + + if (*p == ')') { + + if (paren_depth == 0) { + break; + } + + paren_depth--; + p++; + + continue; + + } + + if (*p == '[') { + + bracket_depth++; + p++; + + continue; + + } + + if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + p++; + continue; + + } + + if (paren_depth == 0 && bracket_depth == 0 && + ((p[0] == '&' && p[1] == '&') || + (p[0] == '|' && p[1] == '|'))) { + return 0; + } + + p++; + + } + + } + + (void) name_len; + return 1; + +} + +static int emit_statement_ident_immediate_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_ident_immediate_compare_now (tok.caret) && + !source_condition_ident_immediate_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("rax"); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now ("rax"); + } + + if (postfix_member_seen && postfix_member_is_unsigned) { + is_unsigned = 1; + } + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int emit_statement_ident_enum_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_ident_enum_compare_now (tok.caret) && !source_condition_ident_enum_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("rax"); + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + /* + * This path is specifically an EAX-vs-constant statement comparison. + * Do not route the RHS through the generic "load constant to named + * register" helper here. During bootstrap the named-register helper path + * is fragile: if the next-stage compiler mis-parses the string argument or + * folds the call badly, enum compares such as: + * + * if (unary_op == TOK_MINUS) + * + * can degrade into: + * + * mov rax, TOK_MINUS + * test rax, rax + * + * which makes every non-zero enum condition true. Emit the RHS into EDX + * directly so the following cmp/jcc sequence is structurally fixed. + */ + if (!emit_statement_rhs_const32_or_enum_to_rdx_if_possible ()) { + return 0; + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int source_condition_member_enum_compare_now (const char *p) { + + char word[128]; + int i; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (strncmp (p, tok.ident, (unsigned long) strlen (tok.ident)) == 0 && !ident_char_now ((unsigned char) p[strlen (tok.ident)])) { + p += strlen (tok.ident); + } else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + } else if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { + return 0; + } + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + return 0; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { + break; + } + + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + i = 0; + + while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && i + 1 < (int) sizeof (word)) { + i++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { + return source_condition_logical_rhs_is_enum_compare_now (p); + } + + return source_condition_tail_end_now (p); + +} + +static int emit_statement_member_enum_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_member_enum_compare_now (tok.caret) && !source_condition_member_enum_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("rax"); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now ("rax"); + } + + if (postfix_member_seen && postfix_member_is_unsigned) { + is_unsigned = 1; + } + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_or_enum_to_rdx_if_possible ()) { + return 0; + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +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_rax (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_rax_rdx_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 rax, rdx\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, " cmpq %%rdx, %%rax\n"); + fprintf (state->ofp, " %s .L%d\n", jmp, label); + + } + +} + +static void emit_statement_cmp64_to_rax (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 rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jb L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jbe L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " ja L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jae L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmp rdx, rcx\n"); + fprintf (state->ofp, " jne L%d\n", false_label); + fprintf (state->ofp, " cmp rax, rbx\n"); + fprintf (state->ofp, " je L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\n"); + fprintf (state->ofp, " jne L%d\n", true_label); + fprintf (state->ofp, " cmp rax, rbx\n"); + fprintf (state->ofp, " jne L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " test rdx, rdx\n"); + fprintf (state->ofp, " jnz L%d\n", true_label); + fprintf (state->ofp, " test rax, rax\n"); + fprintf (state->ofp, " jnz L%d\n", true_label); + break; + + } + + fprintf (state->ofp, "L%d:\n", false_label); + fprintf (state->ofp, " xor rax, rax\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 rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jb .L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jbe .L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " ja .L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\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 rax, rbx\n"); + fprintf (state->ofp, " jae .L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmp rdx, rcx\n"); + fprintf (state->ofp, " jne .L%d\n", false_label); + fprintf (state->ofp, " cmp rax, rbx\n"); + fprintf (state->ofp, " je .L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmp rdx, rcx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + fprintf (state->ofp, " cmp rax, rbx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " test rdx, rdx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " test rax, rax\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + break; + + } + + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xor rax, rax\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, " cmpq %%rcx, %%rdx\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, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " jb .L%d\n", true_label); + break; + + case TOK_LTEQ: + + fprintf (state->ofp, " cmpq %%rcx, %%rdx\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, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " jbe .L%d\n", true_label); + break; + + case TOK_GREATER: + + fprintf (state->ofp, " cmpq %%rcx, %%rdx\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, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " ja .L%d\n", true_label); + break; + + case TOK_GTEQ: + + fprintf (state->ofp, " cmpq %%rcx, %%rdx\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, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " jae .L%d\n", true_label); + break; + + case TOK_EQEQ: + + fprintf (state->ofp, " cmpq %%rcx, %%rdx\n"); + fprintf (state->ofp, " jne .L%d\n", false_label); + fprintf (state->ofp, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " je .L%d\n", true_label); + break; + + case TOK_NOTEQ: + + fprintf (state->ofp, " cmpq %%rcx, %%rdx\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + fprintf (state->ofp, " cmpq %%rbx, %%rax\n"); + fprintf (state->ofp, " jne .L%d\n", true_label); + break; + + default: + + fprintf (state->ofp, " testq %%rdx, %%rdx\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + fprintf (state->ofp, " testq %%rax, %%rax\n"); + fprintf (state->ofp, " jnz .L%d\n", true_label); + break; + + } + + fprintf (state->ofp, ".L%d:\n", false_label); + fprintf (state->ofp, " xorq %%rax, %%rax\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 (double left, enum token_kind op, 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.0; + + } + +} + +static void emit_statement_test_rax_jump_if_false (int label) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " test rax, rax\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, " testq %%rax, %%rax\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, " testq %%%s, %%%s\n", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", nonzero_label); + fprintf (state->ofp, " testq %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " jz .L%d\n", label); + fprintf (state->ofp, ".L%d:\n", nonzero_label); + + } + +} + +static void emit_statement_test_pair_jump_if_true (const char *lo, const char *hi, int label) { + + if (!state->ofp) { + return; + } + + 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"), label); + fprintf (state->ofp, " test %s, %s\n", lo, lo); + 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, " testq %%%s, %%%s\n", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", label); + fprintf (state->ofp, " testq %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " jnz .L%d\n", label); + + } + +} + +static int statement_condition_constant_known = 0; +static int statement_condition_constant_value = 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 ("rax"); + } 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, "rax", 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 ("rax", "rdx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("rax", "rdx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "rax", "rdx"); + } + + } else { + emit_load_global64_to_pair ("rax", "rdx", 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 rax:rdx, + * copy it to the RHS pair rbx:rcx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("rax"); + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name)); + + emit_mov_reg_to_reg_now ("rbx", "rax"); + emit_mov_reg_to_reg_now ("rcx", "rdx"); + + emit_pop_reg_now ("rdx"); + emit_pop_reg_now ("rax"); + + 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, "rax", "rdx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "rax", "rdx"); + } + + } else { + emit_store_pair_to_global64 (name, "rax", "rdx"); + } + + } else { + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg_ex ("rax", lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex ("rax", lhs->offset, lhs->size, lhs->is_unsigned); + } + + } else { + emit_load_global_to_reg ("rax", name, lhs_size); + } + + emit_load_assignment_rhs_to_reg ("rdx"); + 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, "rax"); + } else { + emit_store_reg_to_local (lhs->offset, lhs->size, "rax"); + } + + } else { + emit_store_reg_to_global (name, lhs_size, "rax"); + } + + } + + } 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 = scc_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); + scc_close (step_tmp); + + step_tmp = 0; + + } + + emit_statement_jump (loop_label); + + } + + if (step_tmp) { + scc_close (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 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 rax, rax\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, " testq %%rax, %%rax\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 void emit_statement_rax_truth_jump_if_false_and_tail (int label) { + + int skip_label; + + if (tok.kind == TOK_LOGAND) { + + emit_statement_test_rax_jump_if_false (label); + + get_token (); + emit_statement_jump_if_false (label); + + return; + + } + + if (tok.kind == TOK_LOGOR) { + + skip_label = anon_label++; + emit_test_reg_jump_nonzero_now ("rax", skip_label); + get_token (); + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + return; + + } + + emit_statement_test_rax_jump_if_false (label); + +} + +static int emit_statement_cmp_rax_rdx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label) { + + int rhs_label; + int skip_label; + + if (tok.kind == TOK_LOGAND) { + + get_token (); + + emit_statement_cmp_rax_rdx_jump_if_false (op, is_unsigned, label); + emit_statement_jump_if_false (label); + + return 1; + + } + + if (tok.kind == TOK_LOGOR) { + + rhs_label = anon_label++; + skip_label = anon_label++; + + get_token (); + + emit_statement_cmp_rax_rdx_jump_if_false (op, is_unsigned, rhs_label); + emit_statement_jump (skip_label); + + emit_statement_label (rhs_label); + emit_statement_jump_if_false (label); + + emit_statement_label (skip_label); + return 1; + + } + + emit_statement_cmp_rax_rdx_jump_if_false (op, is_unsigned, label); + return 1; + +} + +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; + } + + /* + * Only claim a single parenthesized assignment here. Do not treat + * a parenthesized logical group whose first term is an assignment as + * one big assignment expression. For example: + * + * ((op2 = get_op (pp)) || (brackets && **pp == ')')) + * + * The old scanner consumed both leading '(' characters, parsed only + * the first assignment, and then tried to manage the outer group's + * closing ')' with parenthesized_assignment_open_parens. That left + * the statement-condition parser out of sync at the final ')' before + * the following '{'. + * + * A real parenthesized-assignment fast path starts with exactly one + * '(' at the current token. Nested/grouped logical expressions must + * be left to the normal expression parser. + */ + parens = 1; + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(') { + return 0; + } + + 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++; + } + + 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 ("rdx", lhs->static_label, DATA_PTR); + } else { + emit_load_local_to_reg ("rdx", lhs->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg ("rdx", name, DATA_PTR); + } + + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_address_to_reg_now ("rdx", lhs->static_label); + } else { + emit_load_local_address_to_reg_now ("rdx", lhs->offset); + } + + } else { + emit_load_address_to_reg_now ("rdx", name); + } + + } + + if (op == TOK_ASSIGN) { + + emit_push_reg_now ("rdx"); + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_load_member_from_addr_reg_now ("rax", "rdx", member_offset, member_size); + emit_push_reg_now ("rdx"); + emit_push_reg_now ("rax"); + + 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 ("rdx", rhs_const); + + } else if (current_integer_expr_is_foldable_now ()) { + + int64_s rhs_const = const64_from_current_foldable_expr (); + emit_load_const32_to_reg_now ("rdx", rhs_const); + + } else { + emit_load_assignment_rhs_expression_to_reg ("rdx"); + } + + emit_pop_reg_now ("rax"); + emit_assignment_binary_op (op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_member_to_addr_reg_now ("rdx", member_offset, "rax", 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 ("rax", "rdx", lhs_is_unsigned); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global64_to_pair ("rax", "rdx", lhs->static_label); + } else { + emit_load_local64_to_pair (lhs->offset, "rax", "rdx"); + } + + } else { + emit_load_global64_to_pair ("rax", "rdx", 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 rax:rdx, + * copy it to the RHS pair rbx:rcx, then restore the original + * LHS value before applying the compound operator. + */ + emit_push_reg_now ("rax"); + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", lhs_is_unsigned); + + emit_mov_reg_to_reg_now ("rbx", "rax"); + emit_mov_reg_to_reg_now ("rcx", "rdx"); + + emit_pop_reg_now ("rdx"); + emit_pop_reg_now ("rax"); + + 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, "rax", "rdx"); + } else { + emit_store_pair_to_local64 (lhs->offset, "rax", "rdx"); + } + + } else { + emit_store_pair_to_global64 (name, "rax", "rdx"); + } + + } 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_ex (reg, lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, lhs->offset, lhs->size, lhs->is_unsigned); + } + + } else { + emit_load_global_to_reg (reg, name, lhs_size); + } + + emit_load_assignment_rhs_to_reg ("rdx"); + + if (strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov rax, %s\n", reg); + } else { + fprintf (state->ofp, " movq %%%s, %%rax\n", reg); + } + + } + + emit_assignment_binary_op (op, lhs_is_unsigned); + + if (strcmp (reg, "rax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, rax\n", reg); + } else { + fprintf (state->ofp, " movq %%rax, %%%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 int source_condition_has_top_level_compare_now (const char *p) { + + int paren_depth = 0; + int bracket_depth = 0; + int saw_operand = 0; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + saw_operand = 1; + continue; + + } + + if (*p == '(') { + + paren_depth++; + + saw_operand = 1; + p++; + + continue; + + } + + if (*p == ')') { + + if (paren_depth == 0) { + return 0; + } + + paren_depth--; + saw_operand = 1; + + p++; + continue; + + } + + if (*p == '[') { + + bracket_depth++; + + saw_operand = 1; + p++; + + continue; + + } + + if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + saw_operand = 1; + p++; + + continue; + + } + + if (paren_depth == 0 && bracket_depth == 0) { + + if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|') || + *p == '?' || *p == ':' || *p == ',' || *p == ';') { + return 0; + } + + if (saw_operand) { + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + return 1; + } + + if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + return 1; + } + + } + + } + + if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + saw_operand = 1; + } + + p++; + + } + + return 0; + +} + +static int source_parenthesized_lhs_followed_by_compare_now (const char *p) { + + int paren_depth = 0; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + continue; + + } + + if (*p == '(') { + + paren_depth++; + p++; + + continue; + + } + + if (*p == ')') { + + paren_depth--; + p++; + + if (paren_depth == 0) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + return 1; + } + + if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + return 1; + } + + return 0; + + } + + if (paren_depth < 0) { + return 0; + } + + continue; + + } + + p++; + + } + + return 0; + +} + +static int emit_statement_parenthesized_lhs_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + if (!source_parenthesized_lhs_followed_by_compare_now (tok.caret)) { + return 0; + } + + get_token (); + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_reg ("rax"); + + expect (TOK_RPAREN, ")"); + + if (!token_is_statement_compare_operator (tok.kind)) { + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return 1; + + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int emit_statement_direct_compare_jump_if_false_now (int label) { + + enum token_kind op; + + int is_unsigned; + int old_assignment32_stop_before_condition_operator; + + if (!source_condition_has_top_level_compare_now (tok.caret) && + !source_condition_has_top_level_compare_now (tok.start)) { + return 0; + } + + /* + * Do not let the generic integer direct-compare fast path consume + * floating conditions such as: + * + * while (value_fractional_d - ((int64_t)value_fractional_d) != 0) + * + * That path lowers the LHS through rax and compares integer dwords, + * so the example above becomes low_word - low_word and the fractional + * loop exits immediately. Leave floating expressions for the x87 + * condition path below. + */ + if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + assignment32_stop_before_condition_operator = 1; + + if (emit_load_assignment_binary_expression_prec_to_reg ("rax", 1)) { + is_unsigned = 1; + } + + assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; + + if (!token_is_statement_compare_operator (tok.kind)) { + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return 1; + + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static void emit_statement_jump_if_false (int label) { + + enum token_kind op; + int is_unsigned; + + const char *call_start; + const char *call_caret; + + char *call_name; + unsigned long call_line; + + int old_assignment32_stop_before_condition_operator; + flush_pending_statement_labels (); + + statement_condition_constant_known = 0; + statement_condition_constant_value = 0; + + if (emit_statement_ident_immediate_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_ident_enum_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_member_enum_compare_jump_if_false_now (label)) { + return; + } + + if (emit_load_parenthesized_assignment_expression_to_reg_now ("rax", &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 ("rdx"); + 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_compare_expression_to_reg ("rdx"); + consume_parenthesized_assignment_remaining_closes (); + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + statement_condition_emit_logical_tail (label); + + return; + + } + + consume_parenthesized_assignment_remaining_closes (); + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return; + + } + + if (emit_statement_parenthesized_lhs_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_direct_compare_jump_if_false_now (label)) { + return; + } + + if (tok.kind == TOK_LPAREN && (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ())) { + + double left_float = 0.0; + + 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 ())) { + + 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 = int64_to_double_now (right_int); + + } + + 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 (floating_rhs_result_in_eax_bool) { + + emit_statement_rax_truth_jump_if_false_and_tail (label); + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + if (left_float_constant) { + + statement_condition_constant_known = 1; + statement_condition_constant_value = left_float != 0.0 ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + emit_load_floating_ld_now (float_size, 0.0); + + emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); + return; + + } + + /* + * Parenthrsized statement conditions must be parsed as ordinary + * expressions here. The older recursive/special-case paths try to split + * things like: + * + * (p - q) > 2 + * ((*u++ = *t++) != '\n') + * (toupper((unsigned char)specifier) == 'X') + * + * into a hand-emitted branch form. That interacts badly with the + * stop-before-condition-operator guard and makes the inner expression + * parser treat non-constant subexpressions as integer constant + * expressions. Let the normal expression parser consume the whole + * parenthesized expression, then branch on the resulting value. If the + * parenthesized value is followed by a comparison, handle that comparison + * normally after the grouped LHS has been loaded. + */ + if (tok.kind == TOK_LPAREN) { + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_reg ("rax"); + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return; + + } + + if (tok.kind == TOK_LPAREN && !source_starts_with_parenthesized_assignment_now ()) { + + get_token (); + + emit_statement_jump_if_false (label); + expect (TOK_RPAREN, ")"); + + statement_condition_emit_logical_tail (label); + return; + } + + if (statement_condition_starts_with_ident_call_now ()) { + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + if (tok.kind != TOK_IDENT) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } else { + + call_name = xstrdup (tok.ident); + call_start = tok.start; + call_caret = tok.caret; + call_line = get_line_number (); + + get_token (); + + 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, "rax", call_start, call_caret, call_line); + free (call_name); + + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return; + + } + + /* + * Fold simple constant conditions at parse time. This avoids emitting: + * + * mov rax, 1 + * test rax, rax + * 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 ()) { + + double left_float = 0.0; + + 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 ())) { + + 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 = int64_to_double_now (right_int); + + } + + 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 (floating_rhs_result_in_eax_bool) { + + emit_statement_rax_truth_jump_if_false_and_tail (label); + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + if (left_float_constant) { + + statement_condition_constant_known = 1; + statement_condition_constant_value = left_float != 0.0 ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + emit_load_floating_ld_now (float_size, 0.0); + emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); + + return; + + } + + /* + * Only take the statement-condition constant shortcut when the current + * token itself is a constant operand. The old text-based fallbacks could + * misfire during self-compilation of enum comparisons such as: + * + * if (assign_op == TOK_ASSIGN) + * + * and reduce the whole condition to the RHS enum value. That generated + * "mov rax, 61; test rax, rax" and made every compound assignment look + * like a plain assignment in the next bootstrap stage. + */ + if (token_is_const_condition_operand_now ()) { + + int fold_whole_expr = current_integer_expr_is_foldable_now (); + 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_rax (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 ("rdx"); + 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_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_test_rax_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 ()) { + + double left_float; + double right_float; + + left_float = int64_to_double_now (left); + 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 (current_integer_expr_is_foldable_now ()) { + 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 ()) { + + double left_float = int64_to_double_now (left); + + 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_rax (left); + emit_load_assignment_rhs_expression_to_reg ("rdx"); + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, 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 ("rax"); + + emit_statement_rax_truth_jump_if_false_and_tail (label); + return; + + } + + if (current_expression_mentions_64bit_symbol_now ()) { + + int old_assignment64_stop_before_condition_operator; + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; + assignment64_stop_before_condition_operator = 1; + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", is_unsigned); + assignment64_stop_before_condition_operator = old_assignment64_stop_before_condition_operator; + + 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) { + + /* + * Save the left-hand 64-bit condition value across loading + * the right-hand side without using three PUSHes. The RHS + * expression may contain a function call, and three pushes + * leave RSP 8 bytes misaligned before the call-frame reserve + * is emitted. Reserve a 32-byte aligned spill area instead. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + amd64_emit_push_reg_now ("rbx"); + amd64_emit_push_reg_now ("rax"); + amd64_emit_push_reg_now ("rdx"); + amd64_emit_push_reg_now ("rcx"); + + } else { + + amd64_emit_push_reg_now ("rbx"); + amd64_emit_push_reg_now ("rax"); + amd64_emit_push_reg_now ("rdx"); + amd64_emit_push_reg_now ("rcx"); + + } + + } + + /* + * The stop-before-compare guard is only needed while loading the + * left hand side of the statement condition. After the condition + * operator has been consumed, the right hand side must be parsed as + * a complete expression. Keeping the guard enabled here makes a + * parenthesized conditional expression such as: + * + * i < (len_fract == 0 ? 1 : precision - len_fract) + * + * stop at the nested == and leave the parser expecting the closing + * ')'. That is what produced the false "expected )" diagnostic. + */ + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", is_unsigned); + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " mov rbx, rax\n"); + fprintf (state->ofp, " mov rcx, rdx\n"); + + amd64_emit_pop_reg_now ("rcx"); + amd64_emit_pop_reg_now ("rdx"); + amd64_emit_pop_reg_now ("rax"); + + } else { + + fprintf (state->ofp, " movq %%rax, %%rbx\n"); + fprintf (state->ofp, " movq %%rdx, %%rcx\n"); + + amd64_emit_pop_reg_now ("rcx"); + amd64_emit_pop_reg_now ("rdx"); + amd64_emit_pop_reg_now ("rax"); + + } + + } + + emit_statement_cmp64_to_rax (op, is_unsigned); + + if (state->ofp) { + amd64_emit_pop_reg_now ("rbx"); + } + + emit_statement_test_rax_jump_if_false (label); + return; + + } + + if (tok.kind == TOK_LOGAND) { + + get_token (); + + emit_statement_test_pair_jump_if_false ("rax", "rdx", label); + emit_statement_jump_if_false (label); + + return; + + } + + if (tok.kind == TOK_LOGOR) { + + int skip_label = anon_label++; + get_token (); + + emit_statement_test_pair_jump_if_true ("rax", "rdx", skip_label); + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + + return; + + } + + emit_statement_test_pair_jump_if_false ("rax", "rdx", label); + return; + + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + assignment32_stop_before_condition_operator = 1; + + emit_load_assignment_rhs_expression_to_reg ("rax"); + assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_rdx_if_possible ()) { + + emit_push_reg_now ("rax"); + emit_load_assignment_compare_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + } + + emit_statement_cmp_rax_rdx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_rax_truth_jump_if_false_and_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 ("rax"); + + 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 ("rdx", 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 (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_now ()) { + + emit_push_reg_now ("rdx"); + emit_load_floating_rhs_expression_now (lvalue_size); + + emit_pop_reg_now ("rdx"); + emit_store_floating_member_to_addr_reg_now ("rdx", 0, lvalue_size); + + return 1; + + } + + if (lvalue_size == (DATA_LLONG & 0x1f)) { + + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", 1); + emit_pop_reg_now ("rcx"); + + emit_store_pair_to_deref_reg_now ("rcx", "rax", "rdx"); + 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 ("rdx"); + + 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, "rax", rhs_start, rhs_caret, rhs_line); + + pending_struct_return_stack_address = 0; + pending_struct_return_stack_offset = 0; + + emit_pop_reg_now ("rdx"); + + free (rhs_name); + return 1; + + } + + if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", 0, lvalue_size)) { + return 1; + } + + /** + * Keep the computed lvalue address across the RHS evaluation. + * The RHS emitter is free to use rdx for indexing, calls, and + * arithmetic, so storing through rdx after it can corrupt memory. + */ + emit_push_reg_now ("rdx"); + + emit_load_assignment_rhs_expression_to_reg ("rax"); + emit_pop_reg_now ("rdx"); + + } else { + + emit_push_reg_now ("rdx"); + + emit_load_member_from_addr_reg_now ("rax", "rdx", 0, lvalue_size); + emit_push_reg_now ("rax"); + + emit_load_assignment_rhs_expression_to_reg ("rdx"); + emit_pop_reg_now ("rax"); + + emit_assignment_binary_op (assign_op, 0); + emit_pop_reg_now ("rdx"); + + } + + emit_store_reg_to_deref_reg_now ("rdx", "rax", 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 = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_tag_name (name) : 0; + + } + + } else if (tok.kind == TOK_DOT) { + + if (src) { + + base_size = src->size; + base_tag_name = src->tag_name; + + } else { + + base_size = get_global_symbol_size (name); + base_tag_name = get_global_symbol_tag_name (name); + + } + + } 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 ("rcx", 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 ("rcx", DATA_PTR & 0x1f); + emit_call_pointer_in_reg_now ("rcx", "rax"); + + 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 ("rax")) { + + 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 ("rax", &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 ("rax"); + 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 rdx, qword [rbp + 16]\n" : " mov rdx, qword ptr [rbp + 16]\n")); + } else { + fprintf (state->ofp, " movq 16(%%rbp), %%rdx\n"); + } + + if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("rdx", 0, current_function_return_size)) { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + } else if (current_function_return_pointer_depth == 0 && current_function_return_size == (DATA_LLONG & 0x1f)) { + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", current_function_return_is_unsigned); + } else { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + } else { + + /* + * Suppressed parsing still has to consume a normal return + * expression. Do not route it through the integer constant + * evaluator: valid returns such as: + * + * return ("text"); + * + * are ordinary pointer expressions, not integer constant + * expressions. Using const_from_current_expr() here makes + * dead/suppressed blocks falsely report "integer constant + * expression expected" at the string literal. + */ + if (current_function_return_pointer_depth == 0 && current_function_return_size == (DATA_LLONG & 0x1f)) { + emit_load_assignment_rhs_expression_to_pair ("rax", "rdx", current_function_return_is_unsigned); + } else { + emit_load_assignment_rhs_expression_to_reg ("rax"); + } + + } + + } + + 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) { + + if (current_function_return_size == (DATA_LLONG & 0x1f)) { + + fprintf (state->ofp, " mov rax, %ld\n", v); + fprintf (state->ofp, " cqo\n"); + + } else { + fprintf (state->ofp, " mov eax, %ld\n", v); + } + + } else { + + if (current_function_return_size == (DATA_LLONG & 0x1f)) { + + fprintf (state->ofp, " movq $%ld, %%rax\n", v); + fprintf (state->ofp, " cqto\n"); + + } else { + fprintf (state->ofp, " movl $%ld, %%eax\n", v); + } + + } + + } + + 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 rsp 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) { + + int target_label = current_break_label; + + long cleanup_base = current_break_cleanup_base; + long cleanup_bytes; + + get_token (); + + if (target_label >= 0) { + + cleanup_bytes = current_block_cleanup_bytes - cleanup_base; + + if (!current_function_uses_single_frame && 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_CONTINUE) { + + int target_label = current_continue_label; + + long cleanup_base = current_continue_cleanup_base; + long cleanup_bytes; + + get_token (); + + if (target_label >= 0) { + + cleanup_bytes = current_block_cleanup_bytes - cleanup_base; + + if (!current_function_uses_single_frame && 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 ("rax"); + + 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, int return_pointer_depth, 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 *function_tmp = 0; + + char *inline_asm_text = 0; + char *function_asm_text = 0; + + int capture_inline_body = 0; + int capture_function_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_return_pointer_depth; + 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; + capture_function_body = saved_ofp != 0; + + if (capture_function_body) { + + function_tmp = scc_tmpfile (); + + if (!function_tmp) { + capture_function_body = 0; + } + + } + + if (emit_body) { + + if (parsed_dllimport) { + report_line_at (get_filename (), last_declarator_name_line, REPORT_ERROR, last_declarator_name_start, last_declarator_name_caret, "function '%s' cannot be defined with '__dllimport'", name); + } + + 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_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, 0); + 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_return_pointer_depth = current_function_return_pointer_depth; + 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_return_pointer_depth = return_pointer_depth; + current_function_returns_aggregate = (!return_is_void && !return_is_floating && return_pointer_depth == 0 && return_size > (DATA_LLONG & 0x1f)); + current_function_calling_convention = (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention; + current_function_param_stack_bytes = 0; + current_function_has_return_statement = 0; + + parse_old_style_param_decls (); + remove_duplicate_pending_params (); + + /* + * Old-style definitions start with an identifier list, so the first + * function-symbol install only knows the default int-sized parameter + * placeholders. The declaration list between ')' and '{' is what gives + * those parameters their real types. Refresh the saved call signature + * after that list has been parsed, otherwise later calls in the same + * translation unit pass 64-bit parameters as single words. + */ + if (emit_body && should_emit) { + copy_pending_params_to_global_symbol (name); + } + + current_function_param_stack_bytes = global_symbol_stdcall_stack_bytes (name); + + 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_function_body) { + state->ofp = function_tmp; + } + + current_return_label = anon_label++; + pending_return_jump = 0; + + current_function_preserve_assignment64_regs = 0; + current_function_is_variadic = saved_declarator_function_is_variadic ? 1 : 0; + + /* + * Do not apply the whole-function frame deferral to inline-function + * capture. The inline expander analyses the captured body as it was + * emitted, including any stack movement before parameter references. + * Moving all automatic storage to a synthetic prologue changes that + * analysis and can corrupt the self-built compiler. + */ + current_function_frame_enabled = (!is_inline && capture_function_body); + emit_function_start (name, !emit_public); + + 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 (); + + current_function_frame_label = -1; + current_function_frame_deferred = 0; + current_function_frame_enabled = 0; + current_function_is_variadic = 0; + + emit_goto_trampolines (); + current_function_uses_single_frame = 0; + + if (capture_function_body && function_tmp) { + + function_asm_text = read_tmp_file_text (function_tmp); + + if (function_asm_text) { + + inline_asm_text = replace_function_frame_placeholder (function_asm_text, current_function_frame_size); + + if (!inline_asm_text) { + + inline_asm_text = function_asm_text; + function_asm_text = 0; + + } + + if (!capture_inline_body || emit_inline_definition_to_output) { + fputs (inline_asm_text, saved_ofp); + } + + if (capture_inline_body) { + + 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); + + } + + } + + if (function_asm_text) { + free (function_asm_text); + } + + if (inline_asm_text) { + + free (inline_asm_text); + inline_asm_text = 0; + + } + + scc_close (function_tmp); + function_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_return_pointer_depth = old_function_return_pointer_depth; + 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; + + } + + clear_pending_params (); + + declarator_function_param_count = 0; + declarator_function_has_prototype = 0; + declarator_function_is_variadic = 0; + + if (tok.kind != TOK_RPAREN) { + + int old_style_param_start = pending_param_count; + + for (;;) { + + if (tok.kind == TOK_ELLIPSIS) { + + declarator_function_is_variadic = 1; + declarator_function_has_prototype = 1; + + get_token (); + + } else if (is_type_start (tok.kind)) { + + int param_base_size; + int param_size; + int param_pointer_depth; + int saved_function_param_count; + + int count_this_param = 0; + int saw_void_param_list = 0; + + char *param_name = 0; + + parse_type_spec (); + param_base_size = parsed_type_size; + + preserve_pending_params++; + + 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 (); + } + + saved_function_param_count = declarator_function_param_count; + + if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) { + parse_declarator (¶m_name); + } + + preserve_pending_params--; + + 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); + param_pointer_depth = declarator_pointer_depth; + + if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { + param_pointer_depth = 1; + } + + if (param_name && find_pending_param_from (param_name, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", param_name); + } else { + + add_pending_param (param_name, param_size, type_alignment (param_size), + (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), + param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); + + } + + } else { + saw_void_param_list = 1; + } + + if (param_name) { + free (param_name); + } + + declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0); + declarator_function_has_prototype = declarator_function_has_prototype || count_this_param || saw_void_param_list; + + } else if (token_is_ident ()) { + + if (find_pending_param_from (tok.ident, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", tok.ident); + } else { + + add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0, 0); + declarator_function_param_count++; + + } + + 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, 0, name_line, name_start, name_caret); + + if (parsed_dllexport || declarator_dllexport) { + + vec_push (&vec_dllexports, xstrdup (name)); + + declarator_dllexport = 0; + parsed_dllexport = 0; + + } + + free (name); + return 1; + + } + + free (name); + return 0; + +} + +static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) { + append_global_init_value (values, max_values, count, value, DATA_CHAR & 0x1f); +} + +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 char masm_pending_data_label[512]; +static char masm_open_data_directive[8]; + +static int masm_has_pending_data_label = 0; +static int masm_data_line_open = 0; +static int masm_data_line_values = 0; + +static void masm_set_pending_data_label (const char *name) { + + const char *asm_name = asm_global_symbol_name (name); + unsigned long len; + + if (!asm_name) { + + masm_has_pending_data_label = 0; + masm_pending_data_label[0] = 0; + + return; + + } + + len = strlen (asm_name); + + if (len >= sizeof (masm_pending_data_label)) { + len = sizeof (masm_pending_data_label) - 1; + } + + memcpy (masm_pending_data_label, asm_name, len); + + masm_pending_data_label[len] = 0; + masm_has_pending_data_label = 1; + +} + +static void masm_flush_data_line (void) { + + if (masm_data_line_open) { + + fprintf (state->ofp, "\n"); + + masm_data_line_open = 0; + masm_data_line_values = 0; + + masm_open_data_directive[0] = 0; + + } + +} + +static void masm_emit_data_prefix (const char *directive) { + + if (masm_has_pending_data_label) { + + masm_flush_data_line (); + fprintf (state->ofp, "%s %s ", masm_pending_data_label, directive); + + masm_has_pending_data_label = 0; + masm_pending_data_label[0] = 0; + + strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); + + masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; + masm_data_line_open = 1; + masm_data_line_values = 0; + + } else if (masm_data_line_open && strcmp (masm_open_data_directive, directive) == 0) { + + if (masm_data_line_values >= 8) { + + fprintf (state->ofp, "\n %s ", directive); + masm_data_line_values = 0; + + } else { + fprintf (state->ofp, ", "); + } + + } else { + + masm_flush_data_line (); + + fprintf (state->ofp, " %s ", directive); + strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); + + masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; + masm_data_line_open = 1; + masm_data_line_values = 0; + + } + + masm_data_line_values++; + +} + +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) { + + if (size == (DATA_CHAR & 0x1f)) { + + masm_emit_data_prefix ("db"); + fprintf (state->ofp, "%ld", low & 0xFFUL); + + } else if (size == (DATA_SHORT & 0x1f)) { + + masm_emit_data_prefix ("dw"); + fprintf (state->ofp, "%ld", low & 0xFFFFUL); + + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + masm_emit_data_prefix ("dq"); + fprintf (state->ofp, "0%08lX%08lXh", high & U32_MASK, low & U32_MASK); + + } else { + + masm_emit_data_prefix ("dd"); + fprintf (state->ofp, "%lu", low & U32_MASK); + + } + + } else if (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) { + + masm_emit_data_prefix ("dq"); + fprintf (state->ofp, "offset %s", asm_symbol); + + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " dq %s\n", asm_symbol); + } else { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " .qaud offset %s\n", asm_symbol); + } else { + fprintf (state->ofp, " .quad %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) { + + masm_emit_data_prefix ("db"); + fprintf (state->ofp, "%ld dup (?)", 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_data_label (const char *name, int is_static) { + + const char *asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_MASM) { + + masm_flush_data_line (); + + if (!is_static) { + fprintf (state->ofp, "public %s\n", asm_name); + } + + masm_set_pending_data_label (name); + + } else { + emit_global_label (name, is_static); + } + +} + +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_data_label (name, is_static); + emit_global_space (total); + + return; + + } + + switch_section (SECTION_DATA); + emit_global_data_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]; + char skip_label[64]; + + int elem_size = DATA_CHAR & 0x1f; + int value_count = 0, i; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (label, "LC%d", anon_label++); + } else { + sprintf (label, ".LC%d", anon_label++); + } + + parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count); + elem_size = parsed_string_initializer_elem_size; + + /* + * 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) { + + 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_data_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], elem_size); + } + + switch_section (SECTION_TEXT); + emit_global_label (skip_label, 1); + + return xstrdup (label); + + } + + switch_section (SECTION_DATA); + emit_global_data_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], elem_size); + } + + 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 = get_global_symbol_dllimport (name) ? asm_global_import_symbol_name (name) : asm_global_symbol_name (name); + + if (get_global_symbol_dllimport (name)) { + + size = DATA_PTR & 0x1f; + is_function = 0; + + } + + 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 declaration_dllimport = 0; + int declaration_dllexport = 0; + + 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_first_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; + declaration_dllimport = parsed_dllimport; + declaration_dllexport = parsed_dllexport; + + consume_post_type_declaration_modifiers_now (&declaration_dllimport, &declaration_dllexport); + parse_declarator (&name); + + declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; + declaration_dllexport = declaration_dllexport || parsed_dllexport || declarator_dllexport; + + 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_first_array_count = declarator_first_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, + (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, + 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 && declarator_has_function && (tok.kind == TOK_LBRACE || is_type_start (tok.kind))) { + + if (declaration_dllexport || parsed_dllexport || declarator_dllexport) { + + vec_push (&vec_dllexports, xstrdup (name)); + + declarator_dllexport = 0; + parsed_dllexport = 0; + declaration_dllexport = 0; + + } + + 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), + (declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0), + 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_first_array_count = decl_first_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. + */ + add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + 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); + + set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, declaration_dllimport); + + copy_pending_params_to_global_symbol (name); + + } + + } else if ((declaration_storage == STORAGE_EXTERN || declaration_dllimport) && init_value_count == 0) { + + add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + set_global_symbol_dllimport (name, declaration_dllimport); + + } + + } 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + + 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_unit64 (void) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + fprintf (state->ofp, ".model flat\n"); + } else if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, "bits 64\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 (); + + } + + if (state->syntax & ASM_SYNTAX_MASM) { + masm_flush_data_line (); + } + + emit_pending_extern_symbols (); + + if (vec_dllexports.length) { + + char *p, *tmp; + + const char *name; + long i; + + FILE *fp; + + if ((p = strrchr (state->ofile, '.'))) { + + tmp = xmalloc (p - state->ofile + 5); + sprintf (tmp, "%.*s.def", (int) (p - state->ofile), state->ofile); + + } else { + + tmp = xmalloc (strlen (state->ofile) + 5); + sprintf (tmp, "%s.def", state->ofile); + + } + + if ((fp = fopen (tmp, "w"))) { + + fprintf (fp, "EXPORTS\n"); + + for (i = 0; i < vec_dllexports.length; i++) { + + if ((name = asm_global_symbol_name (vec_dllexports.data[i]))) { + fprintf (fp, " %s\n", name); + } + + } + + fclose (fp); + + } + + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + + masm_flush_data_line (); + fprintf (state->ofp, "end\n"); + + } + + } + +} diff --git a/cc.c b/cc.c index 74312ad..48dbbce 100755 --- a/cc.c +++ b/cc.c @@ -140,7 +140,12 @@ int main (int argc, char **argv) { return EXIT_FAILURE; } - compile_translation_unit (); + if (state->bits == 32) { + compile_translation_unit32 (); + } else if (state->bits == 64) { + compile_translation_unit64 (); + } + return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/cc.h b/cc.h index d971fae..a014e70 100755 --- a/cc.h +++ b/cc.h @@ -24,7 +24,8 @@ struct cc_state { int pedantic; int mode; - int std; + + int bits, std; int long64; int syntax; diff --git a/i386.c b/i386.c new file mode 100644 index 0000000..c25401d --- /dev/null +++ b/i386.c @@ -0,0 +1,35596 @@ +/****************************************************************************** + * @file i386.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 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); + +static int current_function_preserve_assignment64_regs = 0; +static int current_return_label = 0; + +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 int current_function_param_stack_bytes = 0; + +static enum token_kind current_function_calling_convention = TOK_EOF; + +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 assignment32_stop_before_condition_operator = 0; +static int assignment64_stop_before_condition_operator = 0; + +static void masm_flush_data_line (void); + +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 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 int find_member_info (const char *name, int *offset, int *size) { + return find_member_info_ex (name, offset, size, 0, 0, 0, 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_names[i].calling_convention = TOK_EOF; + + } + + typedef_name_count = 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, enum token_kind calling_convention, 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; + entry->calling_convention = TOK_EOF; + + } + + 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; + entry->calling_convention = calling_convention; + + 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 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; + +} + +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].array_dimensions = 0; + + global_symbols[i].is_dllimport = 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].calling_convention = TOK_EOF; + + { + + int pi; + + for (pi = 0; pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = 0; + global_symbols[i].param_unsigneds[pi] = 0; + global_symbols[i].param_floatings[pi] = 0; + global_symbols[i].param_pointer_depths[pi] = 0; + + } + + } + + global_symbols[i].import_call_stack_bytes = 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 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 int global_symbol_stdcall_stack_bytes (const char *name) { + + int i = find_global_symbol (name); + int pi; + int bytes = 0; + + if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || global_symbols[i].calling_convention != TOK_STDCALL || + + global_symbols[i].is_variadic) { + return 0; + + } + + for (pi = 0; pi < global_symbols[i].param_count && pi < 128; pi++) { + + int size = global_symbols[i].param_sizes[pi]; + + if (size < 1) { + size = DATA_PTR & 0x1f; + } + + bytes += ((size + (DATA_PTR & 0x1f) - 1) / (DATA_PTR & 0x1f)) * (DATA_PTR & 0x1f); + + } + + if (bytes <= 0 && global_symbols[i].import_call_stack_bytes > 0) { + bytes = global_symbols[i].import_call_stack_bytes; + } + + return bytes; + +} + +static void remember_global_symbol_import_call_stack_bytes (const char *name, int bytes) { + + int i = find_global_symbol (name); + + if (i < 0 || bytes <= 0) { + return; + } + + if (global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || !global_symbols[i].is_dllimport || global_symbols[i].calling_convention != TOK_STDCALL) { + return; + } + + if (global_symbol_stdcall_stack_bytes (name) <= 0) { + global_symbols[i].import_call_stack_bytes = bytes; + } + +} + +static const char *asm_global_symbol_name (const char *name) { + + static char buffers[8][512]; + static int index = 0; + + char *out, suffix[32]; + int stdcall_bytes, symbol_index; + + unsigned long avail, len; + + if (asm_symbol_is_internal (name)) { + return name; + } + + index = (index + 1) & 7; + + out = buffers[index]; + len = strlen (name); + + stdcall_bytes = global_symbol_stdcall_stack_bytes (name); + suffix[0] = '\0'; + + symbol_index = find_global_symbol (name); + + if (symbol_index >= 0 && global_symbols[symbol_index].kind == GLOBAL_SYMBOL_FUNCTION && global_symbols[symbol_index].calling_convention == TOK_STDCALL && !global_symbols[symbol_index].is_variadic) { + sprintf (suffix, "@%d", stdcall_bytes); + } + + avail = sizeof (buffers[0]) - 1 - strlen (suffix); + + if (!state->no_leading_underscore) { + + if (avail > 0) { + avail--; + } + + if (len > avail) { + len = avail; + } + + out[0] = '_'; + + memcpy (out + 1, name, len); + out[len + 1] = 0; + + } else { + + if (len > avail) { + len = avail; + } + + memcpy (out, name, len); + out[len] = 0; + + } + + if (suffix[0]) { + strcat (out, suffix); + } + + return out; + +} + +static const char *asm_global_import_symbol_name (const char *name) { + + static char buffers[8][512]; + static int index = 0; + + const char *decorated; + char *out; + + unsigned long len; + + if (!(decorated = asm_global_symbol_name (name)) || asm_symbol_is_internal (name)) { + return decorated; + } + + index = (index + 1) & 7; + + out = buffers[index]; + len = strlen (decorated); + + if (len > sizeof (buffers[0]) - 7) { + len = sizeof (buffers[0]) - 7; + } + + sprintf (out, "__imp_"); + + memcpy (out + 6, decorated, len); + out[len + 6] = 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_dllimport (const char *name, int is_dllimport) { + + int i = find_global_symbol (name); + + if (i >= 0) { + + global_symbols[i].is_dllimport = is_dllimport ? 1 : 0; + + if (is_dllimport) { + global_symbols[i].is_extern = 1; + } + + } + +} + +static int get_global_symbol_dllimport (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].is_dllimport ? 1 : 0; + } + + 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 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_dimensions (const char *name, int dims) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].array_dimensions = dims > 0 ? dims : 0; + } + +} + +static int get_global_symbol_array_dimensions (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].array_dimensions; + } + + 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 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 int get_global_function_returns_floating (const char *name) { + + int i = find_global_symbol (name); + + if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION) { + return 0; + } + + if (global_symbols[i].is_floating) { + return 1; + } + + /* + * Do not infer floating return types from the masked object size. + * + * DATA_INT and DATA_FLOAT both have a 4-byte payload size, so checking + * only (size & 0x1f) makes every int-returning function look like it + * returns float. That breaks expressions such as: + * + * printf("%p", main); + * + * because the function designator gets loaded as a floating object from + * memory instead of as a plain code address. The declaration/definition + * paths already record real floating function returns in is_floating. + */ + return global_symbols[i].is_floating ? 1 : 0; + +} + +static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) { + + int i = find_global_symbol (name); + + if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (i >= 0) { + + global_symbols[i].pointer_depth = pointer_depth; + global_symbols[i].pointed_size = pointed_size; + + global_symbols[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + global_symbols[i].pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; + + } + +} + +static int get_global_symbol_pointed_is_floating (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].pointed_is_floating; + } + + return 0; + +} + +static int get_global_symbol_pointed_is_unsigned (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].pointed_is_unsigned; + } + + return 0; + +} + +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 void set_global_symbol_calling_convention (const char *name, enum token_kind calling_convention) { + + int i = find_global_symbol (name); + + if (i >= 0) { + global_symbols[i].calling_convention = calling_convention; + } + +} + +static enum token_kind get_global_symbol_calling_convention (const char *name) { + + int i = find_global_symbol (name); + + if (i >= 0) { + return global_symbols[i].calling_convention; + } + + return TOK_EOF; + +} + +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 int get_global_symbol_param_size (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128 && global_symbols[i].param_sizes[param_index] > 0) { + return global_symbols[i].param_sizes[param_index]; + } + + return DATA_PTR & 0x1f; + +} + +static int get_global_symbol_param_unsigned (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128) { + return global_symbols[i].param_unsigneds[param_index] ? 1 : 0; + } + + return 0; + +} + +static int get_global_symbol_param_floating (const char *name, int param_index) { + + int i = find_global_symbol (name); + + if (i >= 0 && param_index >= 0 && param_index < 128) { + return global_symbols[i].param_floatings[param_index] ? 1 : 0; + } + + 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 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_dllimport = 0; + 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].pointer_depth = 0; + global_symbols[global_symbol_count].pointed_size = 0; + global_symbols[global_symbol_count].pointed_is_floating = 0; + global_symbols[global_symbol_count].pointed_is_unsigned = 0; + global_symbols[global_symbol_count].returns_void = 0; + global_symbols[global_symbol_count].calling_convention = TOK_EOF; + + { + + int pi; + + for (pi = 0; pi < 128; pi++) { + + global_symbols[global_symbol_count].param_sizes[pi] = 0; + global_symbols[global_symbol_count].param_unsigneds[pi] = 0; + global_symbols[global_symbol_count].param_floatings[pi] = 0; + + } + + } + + global_symbols[global_symbol_count].import_call_stack_bytes = 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].calling_convention = TOK_EOF; + 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); + +static long function_frame_saved_assignment64_bytes (void) { + return current_function_preserve_assignment64_regs ? 12 : 0; +} + +static void emit_function_frame_adjust_text (char **dst, long frame_size) { + + char buf[128]; + long save_bytes; + + int n; + + if (!dst) { + return; + } + + frame_size = (frame_size + 3) & ~3L; + save_bytes = function_frame_saved_assignment64_bytes (); + + if (frame_size <= 0 && save_bytes <= 0) { + return; + } + + if (frame_size > 2147483647L) { + frame_size = 2147483647L; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (frame_size > 0) { + + n = sprintf (buf, " sub esp, %ld\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + if (save_bytes) { + + append_inline_text (dst, " push ebx\n", strlen (" push ebx\n")); + append_inline_text (dst, " push esi\n", strlen (" push esi\n")); + append_inline_text (dst, " push edi\n", strlen (" push edi\n")); + + } + + } else { + + if (frame_size > 0) { + + n = sprintf (buf, " subl $%ld, %%esp\n", frame_size); + append_inline_text (dst, buf, (size_t) n); + + } + + if (save_bytes) { + + append_inline_text (dst, " pushl %ebx\n", strlen (" pushl %ebx\n")); + append_inline_text (dst, " pushl %esi\n", strlen (" pushl %esi\n")); + append_inline_text (dst, " pushl %edi\n", strlen (" pushl %edi\n")); + + } + + } + +} + +static void emit_function_frame_restore_text (char **dst, long frame_size) { + + if (!dst || !function_frame_saved_assignment64_bytes ()) { + return; + } + + (void) frame_size; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + append_inline_text (dst, " pop edi\n", strlen (" pop edi\n")); + append_inline_text (dst, " pop esi\n", strlen (" pop esi\n")); + append_inline_text (dst, " pop ebx\n", strlen (" pop ebx\n")); + + } else { + + append_inline_text (dst, " popl %edi\n", strlen (" popl %edi\n")); + append_inline_text (dst, " popl %esi\n", strlen (" popl %esi\n")); + append_inline_text (dst, " popl %ebx\n", strlen (" popl %ebx\n")); + + } + +} + +static char *replace_function_frame_placeholder (const char *text, long frame_size) { + + const char *frame_marker = "__SCC_FRAME_PLACEHOLDER__\n"; + const char *restore_marker = "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"; + + const char *pr, *pf, *p; + const char *marker, *last; + + char *out = 0; + size_t marker_len; + + if (!text) { + return 0; + } + + last = text; + + for (;;) { + + pf = strstr (last, frame_marker); + pr = strstr (last, restore_marker); + + if (!pf && !pr) { + break; + } + + if (pf && (!pr || pf < pr)) { + p = pf; + marker = frame_marker; + } else { + p = pr; + marker = restore_marker; + } + + marker_len = strlen (marker); + append_inline_text (&out, last, (size_t) (p - last)); + + if (marker == frame_marker) { + emit_function_frame_adjust_text (&out, frame_size); + } else { + emit_function_frame_restore_text (&out, frame_size); + } + + last = p + marker_len; + + } + + append_inline_text (&out, last, strlen (last)); + return out; + +} + +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 + 1UL) & 0xffffffffUL)); + } + + 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); + + } + + } + + scc_close (*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; + +} + +static int local_array_pointer_step_size (const struct local_symbol *sym); +static int global_array_pointer_step_size (const char *name); + +static int aggregate_tag_size_or_zero (const char *tag_name) { + + struct aggregate_tag_entry *entry; + + if (!tag_name || !tag_name[0]) { + return 0; + } + + entry = find_aggregate_tag (tag_name, 0); + + if (entry && entry->size > 0) { + return entry->size; + } + + return 0; + +} + +static long current_local_stack_size = 0; +static long current_block_cleanup_bytes = 0; + +static long current_function_frame_size = 0; + +static int current_function_frame_label = -1; +static int current_function_frame_deferred = 0; +static int current_function_frame_enabled = 0; +static int current_function_uses_single_frame = 0; + +static int next_function_frame_label = 1; + +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; + current_block_cleanup_bytes = 0; + current_function_frame_size = 0; + current_function_uses_single_frame = 0; + current_function_preserve_assignment64_regs = 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].array_dimensions = 0; + local_symbols[local_symbol_count].pointer_depth = 0; + local_symbols[local_symbol_count].pointed_size = 0; + local_symbols[local_symbol_count].pointed_is_floating = 0; + local_symbols[local_symbol_count].pointed_is_unsigned = 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; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_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_symbols[local_symbol_count].array_dimensions = 0; + local_symbols[local_symbol_count].pointer_depth = 0; + local_symbols[local_symbol_count].pointed_size = 0; + local_symbols[local_symbol_count].pointed_is_floating = 0; + local_symbols[local_symbol_count].pointed_is_unsigned = 0; + local_symbols[local_symbol_count].pointed_tag_name = 0; + local_symbols[local_symbol_count].tag_name = 0; + + local_symbol_count++; + +} + +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_array_dimensions (const char *name, int dims) { + + struct local_symbol *sym = find_local_symbol (name); + + if (sym) { + sym->array_dimensions = dims > 0 ? dims : 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; + + } + + } + +} + +static int local_symbol_exists_in_current_scope (const char *name, int start_count); + +static int find_pending_param (const char *name) { + return find_pending_param_from (name, 0); +} + +static void update_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size, int pointed_is_unsigned) { + + int i = find_pending_param (name); + + if (i < 0) { + return; + } + + if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + if (align < 1) { + align = type_alignment (size); + } + + pending_params[i].size = size; + pending_params[i].align = align; + pending_params[i].is_unsigned = is_unsigned ? 1 : 0; + pending_params[i].is_floating = is_floating ? 1 : 0; + pending_params[i].pointer_depth = pointer_depth; + pending_params[i].pointed_size = pointed_size > 0 ? pointed_size : 0; + pending_params[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + pending_params[i].pointed_is_unsigned = pointer_depth > 0 ? (pointed_is_unsigned ? 1 : 0) : 0; + + if (pending_params[i].pointed_tag_name) { + + free (pending_params[i].pointed_tag_name); + pending_params[i].pointed_tag_name = 0; + + } + + if (pointer_depth > 0 && parsed_type_tag_name[0]) { + pending_params[i].pointed_tag_name = xstrdup (parsed_type_tag_name); + } + +} + +static int mark_pending_param_knr_declared (const char *name, const char *name_start, const char *name_caret, unsigned long name_line) { + + int i = find_pending_param (name); + + if (i < 0) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "K&R parameter '%s' is not in function parameter list", name); + return 0; + + } + + if (pending_params[i].knr_declared) { + + report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "duplicate K&R parameter declaration for '%s'", name); + return 0; + + } + + pending_params[i].knr_declared = 1; + return 1; + +} + +static void remove_duplicate_pending_params (void) { + + int i; + + for (i = 0; i < pending_param_count; i++) { + + int j; + + if (!pending_params[i].name) { + continue; + } + + j = i + 1; + + while (j < pending_param_count) { + + if (pending_params[j].name && strcmp (pending_params[i].name, pending_params[j].name) == 0) { + + int k; + + if (pending_params[j].name) { + free (pending_params[j].name); + } + + if (pending_params[j].pointed_tag_name) { + free (pending_params[j].pointed_tag_name); + } + + for (k = j; k + 1 < pending_param_count; k++) { + pending_params[k] = pending_params[k + 1]; + } + + pending_param_count--; + + pending_params[pending_param_count].name = 0; + pending_params[pending_param_count].size = 0; + pending_params[pending_param_count].align = 0; + pending_params[pending_param_count].is_unsigned = 0; + pending_params[pending_param_count].is_floating = 0; + pending_params[pending_param_count].pointer_depth = 0; + pending_params[pending_param_count].pointed_size = 0; + pending_params[pending_param_count].pointed_tag_name = 0; + + continue; + + } + + j++; + + } + + } + +} + +static void copy_pending_params_to_global_symbol (const char *name) { + + int i = find_global_symbol (name); + int pi; + + if (i < 0) { + return; + } + + for (pi = 0; pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = 0; + global_symbols[i].param_unsigneds[pi] = 0; + global_symbols[i].param_floatings[pi] = 0; + global_symbols[i].param_pointer_depths[pi] = 0; + + } + + for (pi = 0; pi < pending_param_count && pi < 128; pi++) { + + global_symbols[i].param_sizes[pi] = pending_params[pi].size; + global_symbols[i].param_unsigneds[pi] = pending_params[pi].is_unsigned; + global_symbols[i].param_floatings[pi] = pending_params[pi].is_floating; + global_symbols[i].param_pointer_depths[pi] = pending_params[pi].pointer_depth; + + } + +} + +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 (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; + } + + if (sym) { + + sym->pointer_depth = pointer_depth; + sym->pointed_size = pointed_size; + + sym->pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + sym->pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; + + if (sym->pointed_tag_name) { + + free (sym->pointed_tag_name); + sym->pointed_tag_name = 0; + + } + + if ((pointer_depth > 0 || sym->is_array) && 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_is_floating = pending_params[i].pointed_is_floating; + local_symbols[local_symbol_count].pointed_is_unsigned = pending_params[i].pointed_is_unsigned; + local_symbols[local_symbol_count].is_array = 0; + local_symbols[local_symbol_count].array_element_size = 0; + local_symbols[local_symbol_count].array_dimensions = 0; + 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 (state->syntax & ASM_SYNTAX_MASM) { + masm_flush_data_line (); + } + + 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 void next (void) { + get_token (); +}*/ + +static int last_deref_cast_type_is_floating = 0; + +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_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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_array_count; + + char *name = 0; + + int size = DATA_INT & 0x1f; + int ok = 0, i; + int cast_is_floating = 0; + + last_deref_cast_type_is_floating = 0; + + 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; + cast_is_floating = parsed_type_is_floating; + + if (tok.kind != TOK_RPAREN) { + + parse_declarator (&name); + + /* + * This parser is called after a leading unary '*', for forms such + * as *(long long *)p. The '*' outside the cast performs the + * dereference, so the size needed by the caller is the pointed-at + * object size, not DATA_PTR. However, forms produced by va_arg() + * for pointer types are one level deeper, e.g. char * becomes + * *(char **). In that case the dereferenced object is itself a + * pointer and must be loaded with pointer width. + */ + if (declarator_is_pointer && declarator_pointer_depth > 1) { + size = DATA_PTR; + } + + } + + if (tok.kind == TOK_RPAREN) { + + get_token (); + ok = 1; + + last_deref_cast_type_is_floating = cast_is_floating; + + } 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_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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + if (out_size) { + *out_size = size; + } + + return ok; + +} + +static void emit_load_assignment_rhs_expression_to_reg (const char *reg); +static void emit_load_floating_rhs_expression_now (int result_size); +static void emit_store_floating_to_local_now (long offset, int size); + +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 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 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_first_array_count = declarator_array_count; + declarator_last_array_count = declarator_array_count; + declarator_array_dimensions = 1; + + 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)) { + + int param_base_size; + + parse_type_spec (); + param_base_size = parsed_type_size; + + for (;;) { + + char *name = 0; + + int param_size; + int param_pointer_depth; + + preserve_pending_params++; + parse_declarator (&name); + preserve_pending_params--; + + if (name) { + + param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size); + param_pointer_depth = declarator_pointer_depth; + + if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { + param_pointer_depth = 1; + } + + if (mark_pending_param_knr_declared (name, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line)) { + + update_pending_param (name, param_size, type_alignment (param_size), + (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), + param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); + + } + + 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_deferred_function_frame_placeholder (void); +static void patch_deferred_function_frame (void); + +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) { + + masm_flush_data_line (); + + 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"); + + emit_deferred_function_frame_placeholder (); + + } else { + + fprintf (state->ofp, "%s:\n", asm_name); + fprintf (state->ofp, " pushl %%ebp\n"); + fprintf (state->ofp, " movl %%esp, %%ebp\n"); + + emit_deferred_function_frame_placeholder (); + + } + +} + +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 emit_deferred_function_frame_placeholder (void) { + + current_function_frame_label = -1; + current_function_frame_deferred = 0; + + if (!state->ofp || !current_function_frame_enabled) { + return; + } + + current_function_frame_label = next_function_frame_label++; + current_function_frame_deferred = 1; + current_function_uses_single_frame = 1; + + fprintf (state->ofp, "__SCC_FRAME_PLACEHOLDER__\n"); + +} + +static void patch_deferred_function_frame (void) { + + current_function_frame_deferred = 0; + current_function_frame_label = -1; + current_function_frame_enabled = 0; + +} + +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_store_floating_to_local_now (long offset, int size) { + + char memref[64]; + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + format_intel_ebp_offset (memref, sizeof (memref), 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, " fstp%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", 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) { + + patch_deferred_function_frame (); + + 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"); + } + + } + + if (current_function_preserve_assignment64_regs) { + fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"); + } + + fprintf (state->ofp, " leave\n"); + + if (current_function_calling_convention == TOK_STDCALL && current_function_param_stack_bytes > 0) { + fprintf (state->ofp, " ret %d\n", current_function_param_stack_bytes); + } else { + 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"); + } + + } + + if (current_function_preserve_assignment64_regs) { + fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"); + } + + fprintf (state->ofp, " leave\n"); + + if (current_function_calling_convention == TOK_STDCALL && current_function_param_stack_bytes > 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " ret %d\n", current_function_param_stack_bytes); + } else { + fprintf (state->ofp, " ret $%d\n", current_function_param_stack_bytes); + } + + } else { + fprintf (state->ofp, " ret\n"); + } + + } + +} + +static void emit_preserve_assignment64_regs (enum token_kind op) { + + if (current_function_frame_deferred) { + + current_function_preserve_assignment64_regs = 1; + return; + + } + + 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 (current_function_frame_deferred) { + return; + } + + 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 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_array_element_size_now (int base_size) { + + int object_size; + + if (!declarator_has_array) { + return 0; + } + + if (declarator_is_pointer) { + return DATA_PTR; + } + + /* + * For an array object, the element reached by one subscript is the + * complete row after the first dimension, not always the scalar base + * type. For example, with: + * + * static const char wday_name[7][3]; + * + * wday_name[i] has type char [3] and decays to the address of that + * three-byte row when passed to %.3s. Recording element size as char + * made expression code emit a byte load from wday_name + i instead of + * the row address wday_name + i * 3. + */ + if (declarator_array_dimensions > 1 && declarator_first_array_count > 0) { + + object_size = declarator_object_size (base_size); + + if (object_size > 0) { + return object_size / declarator_first_array_count; + } + + } + + /* + * For a one-dimensional array, the size of the element reached by one + * subscript is the declared base object size, not the total flattened + * initializer field size. make_declarator_fields() expands objects like + * + * char parmbuf[410]; + * + * into 410 byte fields for initialization/copying purposes. Feeding that + * field total back here made parmbuf[i] scale by 410 and store a dword far + * beyond the stack object. + */ + if (parsed_type_is_aggregate) { + return base_size; + } + + return base_size & 0x1f; + +} + +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 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 current_expression_mentions_64bit_symbol_now (void); +static int rhs_current_operand_is_unsigned_now (void); + +static int64_s parse_floating_const_expr_bits_now (int size); + +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, int object_size, int object_is_floating) { + + 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, object_size, object_is_floating); + + 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; + + } + + } + + if (object_is_floating) { + init->value = parse_floating_const_expr_bits_now (object_size); + } else { + 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], 0, 0); + (*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 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 (current_function_frame_deferred) { + + *block_stack_bytes = needed_stack_bytes; + *block_stack_emitted = (needed_stack_bytes > 0); + + 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 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]; + + int declaration_dllimport = parsed_dllimport; + + for (i = 0; i < MAX_AGG_FIELDS; i++) { + init_symbols[i] = 0; + } + + parse_declarator (&name); + + declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; + 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, + (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, + object_fields, object_field_count); + + } else if (name && (parsed_storage_class == STORAGE_EXTERN || declaration_dllimport)) { + + add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + set_global_symbol_size (name, (declarator_has_function && !declarator_is_pointer && !declarator_function_is_pointer) ? parsed_type_size : ((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 && declarator_function_is_pointer)) ? 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); + + set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, declaration_dllimport); + + copy_pending_params_to_global_symbol (name); + + } + + set_global_symbol_dllimport (name, declaration_dllimport); + 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + 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 & 0x1f); + 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 || parsed_dllimport)) { + + 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], elem_size, 0); + } + + 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; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_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_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && is_string_token ()) { + + int first_init_index = init_count; + int value_count = 0; + + int64_s values[MAX_STRING_INIT_BYTES]; + int i; + + parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); + + if (declarator_array_unsized && value_count > 0 && value_count > object_size) { + + long old_offset = object_offset; + long new_offset; + long delta; + + int adj_i; + current_local_stack_size += value_count - object_size; + + if (current_local_stack_size > current_function_frame_size) { + current_function_frame_size = current_local_stack_size; + } + + new_offset = -current_local_stack_size; + delta = new_offset - old_offset; + object_offset = new_offset; + declarator_array_count = value_count; + + 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; + } + + } + + for (i = 0; i < object_size; i++) { + + if (init_count >= MAX_LOCAL_INITS) { + continue; + } + + inits[init_count].offset = object_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++; + + } + + } 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 if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_has_array) { + + 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 (!declarator_is_pointer && parsed_type_is_floating) { + + emit_load_floating_rhs_expression_now (object_init_size); + emit_store_floating_to_local_now (object_offset, object_init_size); + + } 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], object_init_size, (!declarator_is_pointer && parsed_type_is_floating)); + } + + 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_dimensions (static_label, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (static_label, declarator_array_element_size_now (parsed_type_size)); + + 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + 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); + } + + } + + } + + 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (block_stack_bytes, 1); + } + + if (!is_function_body && !current_function_frame_deferred) { + 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (extra_stack_bytes, 1); + } + + if (!is_function_body && !current_function_frame_deferred) { + 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; + + if (!current_function_frame_deferred) { + emit_stack_adjust (block_stack_bytes, 1); + } + + block_stack_emitted = (block_stack_bytes > 0); + + } else if (needed_stack_bytes > block_stack_bytes) { + + if (!current_function_frame_deferred) { + 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 && !current_function_frame_deferred && 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_ex (const char *reg, long offset, int size, int is_unsigned) { + + char memref[64]; + size &= 0x1f; + + 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 ? " %s %s, byte %s\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", reg, memref); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word %s\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", 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, " %s %ld(%%ebp), %%%s\n", is_unsigned ? "movzbl" : "movsbl", offset, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %ld(%%ebp), %%%s\n", is_unsigned ? "movzwl" : "movswl", offset, reg); + } else { + fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, reg); + } + + } + +} + +static void emit_load_local_to_reg (const char *reg, long offset, int size) { + emit_load_local_to_reg_ex (reg, offset, size, 0); +} + +static void emit_load_global_to_reg_ex (const char *reg, const char *symbol, int size, int is_unsigned) { + + const char *asm_symbol; + size &= 0x1f; + + if (!state->ofp || !reg || !symbol) { + return; + } + + emit_extern_reference_symbol (symbol, size); + + if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { + + asm_symbol = asm_global_import_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"), reg, asm_symbol); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, byte [%s]\n" : " %s %s, byte ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", reg, reg); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr [%s]\n"), reg, reg); + } + + } else { + + fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswl", reg, reg); + } else { + fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, 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 ? " %s %s, byte [%s]\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", reg, asm_symbol); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", 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, " %s %s, %%%s\n", is_unsigned ? "movzbl" : "movsbl", asm_symbol, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %s, %%%s\n", is_unsigned ? "movzwl" : "movswl", asm_symbol, reg); + } else { + fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg); + } + + } + +} + +static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) { + emit_load_global_to_reg_ex (reg, symbol, size, 0); +} + +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; + } + + if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { + + const char *addr_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx"; + + asm_symbol = asm_global_import_symbol_name (symbol); + emit_extern_reference_symbol (symbol, size); + + 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"), addr_reg, asm_symbol); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte [%s], %cl\n" : " mov byte ptr [%s], %cl\n"), addr_reg, 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"), addr_reg, reg[1]); + } else { + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr [%s], %s\n"), addr_reg, reg); + } + + } else { + + fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, addr_reg); + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " movb %%%cl, (%%%s)\n", reg[1], addr_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movw %%%cx, (%%%s)\n", reg[1], addr_reg); + } else { + fprintf (state->ofp, " movl %%%s, (%%%s)\n", reg, addr_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, int is_unsigned) { + + 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, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } + + } else { + fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", base_reg, index_reg, dst_reg); + } + +} + +static void emit_load_indexed_sized_to_reg_ex_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size, int is_unsigned) { + + int scale = 1; + + const char *gasop = is_unsigned ? "movzbl" : "movsbl"; + const char *atype = "byte"; + + if (!state->ofp || !base_reg || !index_reg || !dst_reg) { + return; + } + + elem_size &= 0x1f; + + if (elem_size == (DATA_SHORT & 0x1f)) { + + scale = 2; + + atype = "word"; + gasop = "movswl"; + + } 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, is_unsigned); + } else if (scale == 1) { + + if (strcmp (atype, "byte") == 0) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } + + } else if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, word [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); + } else { + fprintf (state->ofp, " %s %s, word ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", 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 (strcmp (atype, "byte") == 0) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, byte [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " %s %s, byte ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } + + } else if (elem_size == (DATA_SHORT & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_NASM) { + fprintf (state->ofp, " %s %s, word [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); + } else { + fprintf (state->ofp, " %s %s, word ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", 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_indexed_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { + emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 0); +} + +static void emit_load_indexed_unsigned_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { + emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 1); +} + +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 void emit_load_subscript_index_to_reg_now (const char *index_reg) { + + if (current_expression_mentions_64bit_symbol_now ()) { + + int is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); + + if (index_reg && strcmp (index_reg, "eax") != 0) { + emit_mov_reg_to_reg_now (index_reg, "eax"); + } + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (index_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_subscript_index_to_reg_now (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_subscript_index_to_reg_now (index_reg); + + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + + /* + * This is a real multidimensional array row, not an array of + * pointers. For char a[7][3], a[i] is the address of the + * three-byte row. The old code treated every further subscript + * as pointer traversal and loaded *(a + i), which turns the + * first bytes of the row into a bogus pointer. + */ + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + elem_size = index_step_size (pointed_size); + + } else { + + 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 (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { + 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 emit_parse_postfix_subscripts_to_reg_dims_now (const char *reg, int elem_size, int pointer_depth, int pointed_size, int array_dimensions, int is_unsigned) { + + 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) { + + int dims_before = array_dimensions; + + saw_subscript = 1; + get_token (); + + emit_push_reg_now (reg); + emit_load_subscript_index_to_reg_now (index_reg); + + expect (TOK_RBRACK, "]"); + emit_pop_reg_now (reg); + + if (array_dimensions > 0) { + array_dimensions--; + } + + if (tok.kind == TOK_LBRACK) { + + if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + elem_size = index_step_size (pointed_size); + + } else { + + 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 (pointer_depth == 0 && dims_before > 1) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { + emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); + } else { + + if (is_unsigned) { + emit_load_indexed_unsigned_sized_to_reg_now (reg, index_reg, 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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); + } + + } else if (find_global_symbol (name) >= 0) { + emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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_ex (lo, sym->static_label, sym->size, sym->is_unsigned); + } else { + emit_load_local_to_reg_ex (lo, sym->offset, sym->size, sym->is_unsigned); + } + + 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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (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 fold_text_starts_with_type_name_only_before_rparen (const char *p) { + + int saw_type = 0; + char word[64]; + int i; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '*') { + + saw_type = 1; + + p++; + continue; + + } + + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { + break; + } + + 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, "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_type = 1; + + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return saw_type && *p == ')'; + +} + +static int const_integer_expr_text_is_foldable_now (const char *p) { + + int saw_value_token = 0; + int saw_token = 0; + + char word[64]; + int depth = 0, ch, i; + + if (!p) { + return 0; + } + + { + + const char *q = p; + int parens = 0; + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + while (*q == '(') { + + parens = 1; + q++; + + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { + q++; + } + + } + + if (fold_text_starts_with_type_name_only_before_rparen (p) || (parens && fold_text_starts_with_type_name_only_before_rparen (q))) { + 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) { + + /* + * If the caller asks about text that starts inside a cast, the + * scanner can see only the type-name prefix, for example: + * + * unsigned char) ch + * + * That is not an integer constant expression. Returning true + * here sends the parser down expr_const64(), which then reports + * "integer constant expression expected" at the cast. + */ + if (!saw_value_token) { + return 0; + } + + break; + + } + + depth--; + p++; + + continue; + + } + + if (*p >= '0' && *p <= '9') { + + saw_value_token = 1; + saw_token = 1; + + p++; + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + continue; + + } + + if (*p == '\'') { + + saw_value_token = 1; + 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) { + saw_value_token = 1; + } + + 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 int token_kind_is_integer_constant_now (enum token_kind kind) { + + return kind == TOK_CCHAR || kind == TOK_LCHAR || + kind == TOK_CINT || kind == TOK_CUINT || + kind == TOK_CLONG || kind == TOK_CULONG || + kind == TOK_CLLONG || kind == TOK_CULLONG; + +} + +static int token_kind_is_binary_expression_operator_now (enum token_kind kind) { + + return kind == TOK_PLUS || kind == TOK_MINUS || kind == TOK_STAR || + kind == TOK_FSLASH || kind == TOK_MOD || + kind == TOK_AMPER || kind == TOK_PIPE || kind == TOK_CARET || + kind == TOK_LSH || kind == TOK_RSH || + kind == TOK_LESS || kind == TOK_LTEQ || + kind == TOK_GREATER || kind == TOK_GTEQ || + kind == TOK_EQEQ || kind == TOK_NOTEQ || + kind == TOK_LOGAND || kind == TOK_LOGOR; + +} + +static int current_integer_expr_is_foldable_now (void) { + + struct token *saved_tok; + + enum token_kind first_kind; + enum token_kind next_kind; + + if (!const_integer_expr_text_is_foldable_now (tok.start)) { + return 0; + } + + /* + * Macro-expanded integer tokens can have tok.start/tok.caret pointing at + * only the replacement text (for example "16") rather than the complete + * source expression that follows it. Do not hand such a prefix to + * expr_const64() as though it described the whole expression when the real + * token stream continues with a binary operator: + * + * MEMMGR_ALIGN - (size_t)buffer % MEMMGR_ALIGN + * + * The normal expression parser can still handle this; this guard only + * disables the whole-expression constant-folder for that unsafe prefix. + */ + if (!token_kind_is_integer_constant_now (tok.kind)) { + return 1; + } + + first_kind = tok.kind; + + saved_tok = clone_current_token_now (); + get_token (); + + next_kind = tok.kind; + unget_token (saved_tok); + + if (token_kind_is_integer_constant_now (first_kind) && token_kind_is_binary_expression_operator_now (next_kind)) { + return 0; + } + + return 1; + +} + +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_ex_now (const char *reg, int size, int is_unsigned); +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_ex_now (const char *lo, const char *hi, int size, int is_unsigned) { + + 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_ex_now (lo, size, is_unsigned); + + 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 int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating); +static int rhs_current_operand_is_floating_now (void); +static int token_is_floating_constant_now (void); + +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 void emit_load_assignment_rhs_to_reg (const char *reg); +static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi); +static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size); + +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)) { + + if (!cast_is_pointer && !last_cast_type_is_floating && rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + + if (floating_rhs_result_in_eax_bool) { + + if (strcmp (lo, "eax") != 0 && state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov %s, eax\n", lo); + } else { + fprintf (state->ofp, " movl %%eax, %%%s\n", lo); + } + + } + + emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); + floating_rhs_result_in_eax_bool = 0; + + } else { + + emit_floating_stack_to_int_pair_now (lo, hi); + + if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { + emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); + } + + } + + return; + + } + + 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 (tok.kind != TOK_LPAREN && current_integer_expr_is_foldable_now ()) { + + 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_LPAREN) { + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + int cast_deref_size = DATA_INT & 0x1f; + + get_token (); + + if (is_type_start (tok.kind) && parse_deref_cast_type_name (&cast_deref_size)) { + + emit_load_assignment_rhs_to_reg (lo); + + if ((cast_deref_size & 0x1f) == (DATA_LLONG & 0x1f)) { + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + + emit_load_deref_reg_now (lo, cast_deref_size); + emit_extend_pair_high_from_low (lo, hi, cast_deref_size, 1); + + } + + return; + + } + + emit_load_assignment_rhs_expression_to_reg (lo); + expect (TOK_RPAREN, ")"); + + emit_load_deref_reg_now (lo, DATA_INT & 0x1f); + emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1); + + return; + + } + + 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 (); + + src = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (!src && global_index >= 0 && tok.kind == TOK_LPAREN && + get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) { + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + int fptr_depth = get_global_symbol_pointer_depth (name); + int fpointed_size = get_global_symbol_pointed_size (name); + + if (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, lo, name_start, name_caret, name_line); + + if (fptr_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (fptr_depth == 1 && fpointed_size > 0) { + deref_size = fpointed_size & 0x1f; + } + + if (deref_size == (DATA_LLONG & 0x1f)) { + + emit_copy_reg_now (addr_reg, lo); + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + + } else { + + emit_load_deref_reg_now (lo, deref_size); + emit_extend_pair_high_from_low (lo, hi, deref_size, 1); + + } + + free (name); + return; + + } + + if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + postfix_incdec = 1; + postfix_op = tok.kind; + + get_token (); + + } + + 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_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + + get_token (); + emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + + if (va_size == (DATA_LLONG & 0x1f) && va_pointer == 0) { + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + } else { + + emit_copy_reg_now (lo, addr_reg); + emit_load_deref_reg_now (lo, va_size); + + emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); + + } + + 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; + emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + + if (va_size == (DATA_LLONG & 0x1f) && !va_floating && va_pointer == 0) { + emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); + } else { + + emit_copy_reg_now (lo, addr_reg); + emit_load_deref_reg_now (lo, va_size); + emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); + + } + + free (name); + return; + + } + + 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 (get_global_function_returns_floating (name)) { + + if (state->ofp) { + + 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"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", lo); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp + 4]\n" : " mov %s, dword ptr [esp + 4]\n", hi); + fprintf (state->ofp, " add esp, 8\n"); + + } else { + + fprintf (state->ofp, " subl $8, %%esp\n"); + fprintf (state->ofp, " fstpl (%%esp)\n"); + fprintf (state->ofp, " movl (%%esp), %%%s\n", lo); + fprintf (state->ofp, " movl 4(%%esp), %%%s\n", hi); + fprintf (state->ofp, " addl $8, %%esp\n"); + + } + + } + + free (name); + return; + } + + 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 = src->tag_name; + + } 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 && (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_now ())) { + + emit_push_reg_now (lo); + emit_load_floating_rhs_expression_now (lvalue_size); + + emit_pop_reg_now (addr_reg); + emit_store_floating_member_to_addr_reg_now (addr_reg, 0, lvalue_size); + + free (name); + return; + } + + 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_ex_now (lo, hi, source_size, src ? (src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned) : (get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name))); + + 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_ex (lo, src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex (lo, src->offset, src->size, src->is_unsigned); + } + + 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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (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; + + postfix_member_calling_convention = TOK_EOF; + + { + + 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; + postfix_member_calling_convention = last_found_member_calling_convention; + + 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 rhs_current_operand_is_floating_now (void); + +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 () || rhs_current_operand_is_floating_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_ex_now (const char *reg, int size, int is_unsigned) { + + const char *op8; + const char *op16; + + if (!state->ofp) { + return; + } + + op8 = is_unsigned ? "movzx" : "movsx"; + op16 = is_unsigned ? "movzx" : "movsx"; + + if (state->syntax & ASM_SYNTAX_INTEL) { + + if (state->syntax & ASM_SYNTAX_NASM) { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s %s, byte [%s]\n", op8, reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %s, word [%s]\n", op16, reg, reg); + } else { + fprintf (state->ofp, " mov %s, dword [%s]\n", reg, reg); + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s %s, byte ptr [%s]\n", op8, reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s %s, word ptr [%s]\n", op16, reg, reg); + } else { + fprintf (state->ofp, " mov %s, dword ptr [%s]\n", reg, reg); + } + + } + + } else { + + if (size == (DATA_CHAR & 0x1f)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", reg, reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswl", reg, reg); + } else { + fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, reg); + } + + } + +} + +static void emit_load_deref_reg_now (const char *reg, int size) { + emit_load_deref_reg_ex_now (reg, size, 0); +} + +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 pointed_is_unsigned) { + + 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_ex_now (reg, subscript_elem_size, pointer_depth <= 1 ? pointed_is_unsigned : 0); + + 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, " movsx %s, byte [%s + %d]\n", dst_reg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %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, " movsx %s, byte ptr [%s + %d]\n", dst_reg, addr_reg, offset); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movsx %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, " movsbl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg); + } else if (size == (DATA_SHORT & 0x1f)) { + fprintf (state->ofp, " movswl %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); + + if (src->is_array && !src->pointer_depth) { + current_object_tag_name = src->tag_name; + } else if (!src->is_array && src->pointer_depth > 0) { + current_object_tag_name = src->pointed_tag_name; + } + + } 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 (get_global_symbol_array (src_name) && !get_global_symbol_pointer_depth (src_name)) { + current_object_tag_name = get_global_symbol_tag_name (src_name); + } else if (!get_global_symbol_array (src_name) && get_global_symbol_pointer_depth (src_name) > 0) { + current_object_tag_name = get_global_symbol_tag_name (src_name); + } + + } + + if (elem_size <= 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_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_add_const_to_reg_now (const char *reg, int offset) { + + if (!state->ofp || !reg || offset == 0) { + return; + } + + 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 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 void save_parse_type_state_now ( + + int *saved_type_size, + int *saved_storage_class, + int *saved_is_aggregate, + int *saved_is_void, + int *saved_is_unsigned, + int *saved_is_floating, + int *saved_has_tag, + int *saved_is_inline, + int *saved_field_count, + int saved_fields[MAX_AGG_FIELDS], + int *saved_declarator_is_pointer, + int *saved_declarator_pointer_depth, + int *saved_declarator_has_array, + int *saved_declarator_has_function, + int *saved_declarator_array_unsized, + int *saved_declarator_array_dimensions, + long *saved_declarator_array_count, + long *saved_declarator_first_array_count) { + + int i; + + *saved_type_size = parsed_type_size; + *saved_storage_class = parsed_storage_class; + *saved_is_aggregate = parsed_type_is_aggregate; + *saved_is_void = parsed_type_is_void; + *saved_is_unsigned = parsed_type_is_unsigned; + *saved_is_floating = parsed_type_is_floating; + *saved_has_tag = parsed_type_has_tag; + *saved_is_inline = parsed_type_is_inline; + *saved_field_count = parsed_field_count; + + for (i = 0; i < *saved_field_count && i < MAX_AGG_FIELDS; i++) { + saved_fields[i] = parsed_field_sizes[i]; + } + + *saved_declarator_is_pointer = declarator_is_pointer; + *saved_declarator_pointer_depth = declarator_pointer_depth; + *saved_declarator_has_array = declarator_has_array; + *saved_declarator_has_function = declarator_has_function; + *saved_declarator_array_unsized = declarator_array_unsized; + *saved_declarator_array_dimensions = declarator_array_dimensions; + *saved_declarator_array_count = declarator_array_count; + *saved_declarator_first_array_count = declarator_first_array_count; + +} + +static void restore_parse_type_state_now ( + int saved_type_size, + int saved_storage_class, + int saved_is_aggregate, + int saved_is_void, + int saved_is_unsigned, + int saved_is_floating, + int saved_has_tag, + int saved_is_inline, + int saved_field_count, + int saved_fields[MAX_AGG_FIELDS], + int saved_declarator_is_pointer, + int saved_declarator_pointer_depth, + int saved_declarator_has_array, + int saved_declarator_has_function, + int saved_declarator_array_unsized, + int saved_declarator_array_dimensions, + long saved_declarator_array_count, + long saved_declarator_first_array_count) { + + int i; + + 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_dimensions = saved_declarator_array_dimensions; + declarator_array_count = saved_declarator_array_count; + declarator_first_array_count = saved_declarator_first_array_count; + +} + +static int parse_builtin_va_arg_type_now (int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { + + int saved_type_size; + int saved_storage_class; + int saved_is_aggregate; + int saved_is_void; + int saved_is_unsigned; + int saved_is_floating; + int saved_has_tag; + int saved_is_inline; + int saved_field_count; + int saved_fields[MAX_AGG_FIELDS]; + int saved_declarator_is_pointer; + int saved_declarator_pointer_depth; + int saved_declarator_has_array; + int saved_declarator_has_function; + int saved_declarator_array_unsized; + int saved_declarator_array_dimensions; + + long saved_declarator_array_count; + long saved_declarator_first_array_count; + + char *type_name = 0; + + int base_size; + int pointer_depth; + int is_unsigned; + int is_floating; + + save_parse_type_state_now (&saved_type_size, &saved_storage_class, + &saved_is_aggregate, &saved_is_void, &saved_is_unsigned, + &saved_is_floating, &saved_has_tag, &saved_is_inline, + &saved_field_count, saved_fields, &saved_declarator_is_pointer, + &saved_declarator_pointer_depth, &saved_declarator_has_array, + &saved_declarator_has_function, &saved_declarator_array_unsized, + &saved_declarator_array_dimensions, &saved_declarator_array_count, + &saved_declarator_first_array_count); + + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; + declarator_first_array_count = 1; + declarator_array_dimensions = 0; + + parse_type_spec (); + base_size = parsed_type_size & 0x1f; + + is_unsigned = parsed_type_is_unsigned; + is_floating = parsed_type_is_floating; + + if (tok.kind != TOK_RPAREN) { + parse_declarator (&type_name); + } + + pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0; + + if (type_name) { + free (type_name); + } + + if (out_pointer) { + *out_pointer = pointer_depth; + } + + if (out_unsigned) { + *out_unsigned = is_unsigned; + } + + if (out_floating) { + *out_floating = is_floating; + } + + if (out_size) { + *out_size = pointer_depth > 0 ? (DATA_PTR & 0x1f) : base_size; + } + + restore_parse_type_state_now (saved_type_size, saved_storage_class, + saved_is_aggregate, saved_is_void, saved_is_unsigned, + saved_is_floating, saved_has_tag, saved_is_inline, + saved_field_count, saved_fields, saved_declarator_is_pointer, + saved_declarator_pointer_depth, saved_declarator_has_array, + saved_declarator_has_function, saved_declarator_array_unsized, + saved_declarator_array_dimensions, saved_declarator_array_count, + saved_declarator_first_array_count); + + return 1; + +} + +static int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { + + char *name; + + const char *name_start; + const char *name_caret; + const char *scratch_reg; + + unsigned long name_line; + unsigned long inc; + + struct local_symbol *src; + + int global_index; + int size = DATA_INT & 0x1f; + int is_unsigned = 1; + int pointer_depth = 0; + int is_floating = 0; + int deref_lvalue = 0; + int paren_lvalue = 0; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + paren_lvalue = 1; + get_token (); + + } + + if (tok.kind == TOK_STAR) { + + deref_lvalue = 1; + 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 va_list object in __scc_builtin_va_arg"); + return 1; + + } + + name = xstrdup (tok.ident); + get_token (); + + if (paren_lvalue) { + expect (TOK_RPAREN, ")"); + } + + expect (TOK_COMMA, ","); + + if (!is_type_start (tok.kind)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type name in __scc_builtin_va_arg"); + + free (name); + return 1; + + } + + parse_builtin_va_arg_type_now (&size, &is_unsigned, &pointer_depth, &is_floating); + expect (TOK_RPAREN, ")"); + + if (size <= 0) { + size = DATA_INT & 0x1f; + } + + inc = (unsigned long) size; + + 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) { + + scratch_reg = strcmp (reg, "edx") == 0 ? "ecx" : "edx"; + + if (src) { + + if (src->is_static && src->static_label) { + emit_load_global_to_reg (scratch_reg, src->static_label, DATA_PTR); + } else { + emit_load_local_to_reg (scratch_reg, src->offset, DATA_PTR); + } + + } else { + emit_load_global_to_reg (scratch_reg, name, DATA_PTR); + } + + if (state->ofp) { + + 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", reg, scratch_reg); + fprintf (state->ofp, " push %s\n", reg); + fprintf (state->ofp, " add %s, %lu\n", reg, inc); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr [%s], %s\n", scratch_reg, reg); + fprintf (state->ofp, " pop %s\n", reg); + + } else { + + fprintf (state->ofp, " movl (%%%s), %%%s\n", scratch_reg, reg); + fprintf (state->ofp, " pushl %%%s\n", reg); + fprintf (state->ofp, " addl $%lu, %%%s\n", inc, reg); + fprintf (state->ofp, " movl %%%s, (%%%s)\n", reg, scratch_reg); + fprintf (state->ofp, " subl $%lu, %%%s\n", inc, reg); + fprintf (state->ofp, " popl %%%s\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, " push %s\n", reg); + fprintf (state->ofp, " add %s, %lu\n", reg, inc); + + } else { + + fprintf (state->ofp, " pushl %%%s\n", reg); + fprintf (state->ofp, " addl $%lu, %%%s\n", inc, 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 (state->ofp) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " pop %s\n", reg); + } else { + fprintf (state->ofp, " popl %%%s\n", reg); + } + + } + + } + + if (out_size) { + *out_size = size; + } + + if (out_unsigned) { + *out_unsigned = is_unsigned; + } + + if (out_pointer) { + *out_pointer = pointer_depth; + } + + if (out_floating) { + *out_floating = is_floating; + } + + 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_scale_reg_by_const_now (const char *reg, int scale); + +static int source_starts_deref_assignment_at_now (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 == '=' && p[1] != '=') { + return 1; + } + + if ((p[0] == '+' || p[0] == '-' || p[0] == '*' || p[0] == '/' || + p[0] == '%' || p[0] == '&' || p[0] == '^' || p[0] == '|') && + p[1] == '=') { + return 1; + } + + if ((p[0] == '<' && p[1] == '<' && p[2] == '=') || + (p[0] == '>' && p[1] == '>' && p[2] == '=')) { + return 1; + } + + return 0; + +} + +static int emit_load_deref_assignment_expression_to_reg_now (const char *reg) { + + char *name; + + const char *name_start; + const char *name_caret; + + unsigned long name_line; + + struct local_symbol *sym; + int global_index; + + enum token_kind op; + + int pointer_depth = 0; + int pointed_size = DATA_INT & 0x1f; + int value_pointer_depth = 0; + int store_size = DATA_INT & 0x1f; + + if (tok.kind != TOK_STAR) { + return 0; + } + + if (!source_starts_deref_assignment_at_now (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 (!is_assignment_operator (tok.kind)) { + + free (name); + return 0; + + } + + op = tok.kind; + get_token (); + + sym = find_local_symbol (name); + global_index = find_global_symbol (name); + + if (sym) { + + pointer_depth = sym->pointer_depth; + pointed_size = sym->pointed_size; + + 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 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 ("ecx", 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; + + } + + value_pointer_depth = pointer_depth - 1; + + if (value_pointer_depth > 0) { + store_size = DATA_PTR & 0x1f; + } else if (pointed_size > 0) { + store_size = pointed_size & 0x1f; + } + + if (store_size <= 0) { + store_size = DATA_INT & 0x1f; + } + + emit_push_reg_now ("ecx"); + + if (op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + emit_load_deref_reg_now ("ecx", store_size); + + emit_push_reg_now ("ecx"); + emit_load_assignment_rhs_expression_to_reg ("edx"); + + emit_pop_reg_now ("eax"); + + if ((op == TOK_PLUSEQ || op == TOK_MINUSEQ) && value_pointer_depth > 0 && + index_step_size (pointed_size) > 1) { + emit_scale_reg_by_const_now ("edx", index_step_size (pointed_size)); + } + + emit_assignment_binary_op (op, 0); + + } + + emit_pop_reg_now ("ecx"); + emit_store_reg_to_deref_reg_now ("ecx", "eax", store_size); + + if (reg && 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); + } + + } + + expect (TOK_RPAREN, ")"); + + if (value_pointer_depth > 0) { + set_rhs_last_pointer_info (value_pointer_depth, pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + free (name); + return 1; + +} + +static int source_starts_double_deref_at_now (const char *p) { + + if (!p || *p != '*') { + return 0; + } + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '*'; + +} + +static int current_floating_token_is_nonzero_now (void) { + + if (tok.kind == TOK_CFLOAT) { + + union { float f; unsigned long u; } v; + + v.u = 0; + v.f = tok.val.f; + + return (v.u & 0x7fffffffUL) != 0; + + } + + { + + union { double d; unsigned long u[2]; } v; + + v.u[0] = 0; + v.u[1] = 0; + + if (tok.kind == TOK_CLDOUBLE) { + v.d = tok.val.ld; + } else { + v.d = tok.val.d; + } + + return ((v.u[1] & 0x7fffffffUL) != 0 || v.u[0] != 0); + + } + +} + +static void emit_load_assignment_rhs_to_reg (const char *reg) { + + clear_rhs_last_pointer_info (); + + if (tok.kind == TOK_STAR && source_starts_double_deref_at_now (tok.caret)) { + + int deref_size; + int inner_pointer_depth; + int inner_pointed_size; + + get_token (); + emit_load_assignment_rhs_to_reg (reg); + + inner_pointer_depth = rhs_last_pointer_depth; + inner_pointed_size = rhs_last_pointed_size; + + if (inner_pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (inner_pointer_depth == 1 && inner_pointed_size > 0) { + deref_size = inner_pointed_size & 0x1f; + } else { + deref_size = DATA_INT & 0x1f; + } + + emit_load_deref_reg_now (reg, deref_size); + + if (inner_pointer_depth > 1) { + set_rhs_last_pointer_info (inner_pointer_depth - 1, inner_pointed_size); + } else { + clear_rhs_last_pointer_info (); + } + + 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 (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; + + if (emit_load_deref_assignment_expression_to_reg_now (reg)) { + return; + } + + /* + * 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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_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; + declarator_first_array_count = 1; + + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + 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 (tok.kind != TOK_LPAREN && current_integer_expr_is_foldable_now ()) { + + 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 () || rhs_current_operand_is_floating_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 ? src->pointed_tag_name : src->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->is_array ? (src->tag_name ? src->tag_name : src->pointed_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; + int member_load_size = DATA_PTR & 0x1f; + + 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 > 0) { + member_load_size = DATA_PTR & 0x1f; + } else { + member_load_size = member_size; + } + + 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 && offset != 0) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " add edx, %d\n", offset); + } else { + fprintf (state->ofp, " addl $%d, %%edx\n", offset); + } + + } + + 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, member_load_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 (member_size > (DATA_PTR & 0x1f) && member_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); + } + + } + + } else { + emit_load_member_from_addr_reg_now (reg, reg, offset, member_load_size); + } + + if (lhs_has_postfix) { + /* Already applied above only for assignment forms. */ + } + + if (member_pointer_depth > 0) { + set_rhs_last_pointer_info (member_pointer_depth, member_elem_size); + } else { + clear_rhs_last_pointer_info (); + } + + 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, lhs_sym->pointed_is_unsigned)) { + + 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), get_global_symbol_pointed_is_unsigned (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)) { + + /* + * 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; + + } + + /* + * parse_deref_cast_type_name() consumes only the cast type + * parentheses. The operand being cast is still the current + * token, e.g. the ptr in: + * + * *(size_t *)ptr + sizeof(size_t) + * + * Load that address expression before applying the outer + * unary '*'. Otherwise the caller sees the unconsumed + * identifier/operator and reports a false "expected )". + */ + emit_load_assignment_rhs_to_reg (reg); + 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 (is_assignment_operator (tok.kind)) { + + enum token_kind assign_op = tok.kind; + + emit_push_reg_now (reg); + get_token (); + + if (assign_op == TOK_ASSIGN) { + emit_load_assignment_rhs_expression_to_reg (reg); + } else { + + emit_load_deref_reg_now (reg, deref_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, 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; + int64_s v; + + size_t len = tok.ident ? strlen (tok.ident) : 0; + + v.low = current_floating_token_is_nonzero_now () ? 1 : 0; + v.high = 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_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + get_token (); + + emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_deref_reg_now (reg, va_size); + + if (va_pointer > 0) { + set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); + } else { + + emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); + clear_rhs_last_pointer_info (); + + } + + 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 (); + + struct local_symbol *src; + int postfix_incdec = 0; + + get_token (); + + if (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_INT & 0x1f; + int va_unsigned = 1; + int va_pointer = 0; + int va_floating = 0; + + emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_deref_reg_now (reg, va_size); + + if (va_pointer > 0) { + set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); + } else { + + emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); + clear_rhs_last_pointer_info (); + + } + + free (name); + return; + + } + + { + + 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 saw_subscript; + 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 && src->pointer_depth == 0 && elem_size <= (DATA_PTR & 0x1f)) { + + int aggregate_elem_size = aggregate_tag_size_or_zero (src->tag_name ? src->tag_name : src->pointed_tag_name); + + if (aggregate_elem_size > (DATA_PTR & 0x1f)) { + elem_size = aggregate_elem_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); + } + + saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, src->pointer_depth, src->pointed_size, src->array_dimensions, src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned); + + postfix_copy_lvalue_size = index_step_size (elem_size); + postfix_copy_lvalue_tag_name = src->is_array ? (src->tag_name ? src->tag_name : src->pointed_tag_name) : src->pointed_tag_name; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + postfix_copy_lvalue_tag_name = 0; + + 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { + + postfix_member_seen = 1; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = elem_size; + postfix_member_size = elem_size; + postfix_member_is_floating = src->is_floating; + postfix_member_is_unsigned = src->is_unsigned; + + } + + 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 saw_subscript; + int elem_size; + + { + + 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 (get_global_symbol_array (name) && get_global_symbol_pointer_depth (name) == 0 && elem_size <= (DATA_PTR & 0x1f)) { + + int aggregate_elem_size = aggregate_tag_size_or_zero (get_global_symbol_tag_name (name)); + + if (aggregate_elem_size > (DATA_PTR & 0x1f)) { + elem_size = aggregate_elem_size; + } + + } + + 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); + } + + saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name), get_global_symbol_array_dimensions (name), get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name)); + + postfix_copy_lvalue_size = index_step_size (elem_size); + postfix_copy_lvalue_tag_name = get_global_symbol_array (name) ? get_global_symbol_tag_name (name) : 0; + + } + + emit_apply_postfix_member_access_to_reg_now (reg); + postfix_copy_lvalue_tag_name = 0; + + 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { + + postfix_member_seen = 1; + postfix_member_pointer_depth = 0; + postfix_member_pointed_size = elem_size; + postfix_member_size = elem_size; + postfix_member_is_floating = get_global_symbol_floating (name); + postfix_member_is_unsigned = get_global_symbol_unsigned (name); + + } + + 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_ex (reg, dst->static_label, dst->size, dst->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, dst->offset, dst->size, dst->is_unsigned); + } + + } else { + emit_load_global_to_reg_ex (reg, name, dst_size, get_global_symbol_unsigned (name)); + } + + 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_ex (reg, src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, src->offset, src->size, src->is_unsigned); + } + + 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_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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 = get_global_symbol_tag_name (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 (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 = (double) tok.val.ld; + } else { + f = 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 double floating_constant_to_ld_now (void) { + + double v; + + if (tok.kind == TOK_CFLOAT) { + v = (float) tok.val.f; + } else if (tok.kind == TOK_CLDOUBLE) { + v = (double) tok.val.ld; + } else { + v = tok.val.d; + } + + get_token (); + return v; + +} + +static double int64_u32_base_now (void) { + + volatile unsigned long half; + double d; + + /* + * Build 2^32 without a direct large floating literal. The volatile word + * prevents the self compiler from folding this back into an LC*_flt data + * constant while compiling parse.c. + */ + half = 65536UL; + + d = (double) half; + d *= (double) half; + + return d; + +} + +static double int64_to_double_now (int64_s v) { + + double d; + + d = (double) v.high; + d *= int64_u32_base_now (); + d += (double) v.low; + + return d; + +} + +static double parse_floating_const_expr_value_now (void); +static double parse_floating_const_term_now (void); +static double parse_floating_const_primary_now (void); + +static double parse_floating_const_primary_now (void) { + + 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 (); + return int64_to_double_now (iv); + +} + +static double parse_floating_const_term_now (void) { + + double rhs, v; + enum token_kind op; + + 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 double parse_floating_const_expr_value_now (void) { + + double rhs, v; + enum token_kind op; + + 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) { + + 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; + + } + + { + + unsigned char bytes[8]; + + double d = (double) acc; + int i; + + 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) { + + switch_section (SECTION_DATA); + + if (size == (DATA_DOUBLE & 0x1f)) { + + /* + * Emit the exact IEEE bits as a 64-bit hex integer. The old + * decimal concatenated the high and low dwords as text, and the + * high==0 branch emitted only a single dd even though the later + * load is fld qword ptr. Both forms are unstable across + * bootstrap runs. + */ + fprintf (state->ofp, "LC%d_flt dq 0%08lX%08lXh\n", lab, v.high & U32_MASK, v.low & U32_MASK); + + } else { + fprintf (state->ofp, "LC%d_flt dd 0%08lXh\n", lab, v.low & U32_MASK); + } + + switch_section (SECTION_TEXT); + fprintf (state->ofp, " fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + if (state->syntax & ASM_SYNTAX_NASM) { + + switch_section (SECTION_DATA); + + 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); + } + + switch_section (SECTION_TEXT); + fprintf (state->ofp, " fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); + + } else { + + switch_section (SECTION_DATA); + + 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); + } + + switch_section (SECTION_TEXT); + + 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_ex ("eax", src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex ("eax", src->offset, src->size, src->is_unsigned); + } + + } else { + emit_load_global_to_reg_ex ("eax", name, size, get_global_symbol_unsigned (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"); + + } + +} + +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 int emit_statement_rhs_const32_to_edx_if_possible (void); + +static void emit_statement_label (int label); +static void emit_statement_label_raw (int label); +static void emit_statement_jump (int label); + +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_fild_eax_now (void) { + + 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 void emit_load_any_deref_as_floating_now (const char *reg, int size, int is_floating) { + + if (is_floating) { + + emit_load_floating_deref_reg_now (reg, size); + return; + + } + + emit_load_deref_reg_now (reg, size); + emit_fild_eax_now (); + +} + +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; + int deref_is_floating = 1; + + get_token (); + + if (tok.kind == TOK_LPAREN) { + + get_token (); + + if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) { + + emit_load_any_deref_as_floating_now ("eax", deref_size, last_deref_cast_type_is_floating); + return; + + } + + emit_load_assignment_rhs_expression_to_reg ("eax"); + expect (TOK_RPAREN, ")"); + + if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { + + deref_size = rhs_last_pointed_size; + deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); + + } + + emit_load_any_deref_as_floating_now ("eax", deref_size, deref_is_floating); + return; + + } + + emit_load_assignment_rhs_to_reg ("eax"); + + if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { + + deref_size = rhs_last_pointed_size; + deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); + + } + + emit_load_any_deref_as_floating_now ("eax", deref_size, deref_is_floating); + 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)) { + + if (!cast_is_pointer && !last_cast_type_is_floating) { + + if (rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_operand_now (result_size); + + if (floating_rhs_result_in_eax_bool) { + + emit_extend_pair_high_from_low ("eax", "edx", cast_size, cast_is_unsigned); + floating_rhs_result_in_eax_bool = 0; + + } else { + emit_floating_stack_to_int_pair_now ("eax", "edx"); + } + + if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { + emit_extend_pair_high_from_low ("eax", "edx", cast_size, cast_is_unsigned); + } + + } else { + emit_load_assignment_rhs_to_pair ("eax", "edx"); + } + + emit_integer_pair_to_floating_stack_now ("eax", "edx", cast_size); + return; + + } + + 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_SCC_BUILTIN_VA_ARG) { + + int va_size = DATA_DOUBLE & 0x1f; + int va_unsigned = 0; + int va_pointer = 0; + int va_floating = 0; + + get_token (); + + emit_parse_builtin_va_arg_address_to_reg_now ("eax", &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_floating_deref_reg_now ("eax", va_size); + + 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { + + int va_size = DATA_DOUBLE & 0x1f; + int va_unsigned = 0; + int va_pointer = 0; + int va_floating = 0; + + emit_parse_builtin_va_arg_address_to_reg_now ("eax", &va_size, &va_unsigned, &va_pointer, &va_floating); + emit_load_floating_deref_reg_now ("eax", va_size); + + free (name); + return; + + } + + 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_function_returns_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_LBRACK && (src || find_global_symbol (name) >= 0)) { + + int elem_size; + int pointer_depth; + int pointed_size; + + 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); + + pointer_depth = src->pointer_depth; + pointed_size = src->pointed_size; + + if (src->is_array) { + + if (src->is_static && src->static_label) { + emit_load_symbol_address_to_reg_now ("eax", src->static_label, 0, 0); + } else { + emit_load_symbol_address_to_reg_now ("eax", 0, src->offset, 1); + } + + } else 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 { + + 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)); + + pointer_depth = get_global_symbol_pointer_depth (name); + pointed_size = get_global_symbol_pointed_size (name); + + if (get_global_symbol_array (name)) { + emit_load_symbol_address_to_reg_now ("eax", name, 0, 0); + } else { + emit_load_global_to_reg ("eax", name, DATA_PTR); + } + + } + + if (elem_size <= 0) { + elem_size = DATA_INT & 0x1f; + } + + emit_parse_postfix_subscripts_to_reg_now ("eax", elem_size, pointer_depth, pointed_size); + + if ((elem_size & 0x1f) > DATA_PTR) { + emit_load_floating_deref_reg_now ("eax", elem_size); + } else { + emit_fild_eax_now (); + } + + 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; + const char *current_tag_name; + + 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 current_size; + + if (src) { + + current_size = src->size; + current_tag_name = src->tag_name; + + } else { + + current_size = get_global_symbol_size (name); + current_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, current_size, current_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 (tok.kind != TOK_DOT && tok.kind != TOK_ARROW && member_is_floating) { + + emit_load_floating_member_symbol_now (src, name, member_offset, member_size); + + free (name); + return; + + } + + emit_load_symbol_address_for_copy_now ("eax", src, name); + emit_add_const_to_reg_now ("eax", member_offset); + + current_tag_name = last_found_member_tag_name; + + if (member_pointer_depth > 0 || member_is_array) { + current_size = member_elem_size; + } else { + current_size = member_size; + } + + while (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) { + + enum token_kind 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; + + } + + member = xstrdup (tok.ident); + get_token (); + + if (!find_member_info_ex_bounded (member, current_size, current_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 (member_op == TOK_ARROW) { + emit_load_deref_reg_now ("eax", DATA_PTR & 0x1f); + } + + emit_add_const_to_reg_now ("eax", member_offset); + + current_tag_name = last_found_member_tag_name; + + if (member_pointer_depth > 0 || member_is_array) { + current_size = member_elem_size; + } else { + current_size = member_size; + } + + } + + if (member_is_floating) { + emit_load_floating_member_from_addr_reg_now ("eax", 0, member_size); + } else { + + emit_load_deref_reg_now ("eax", member_size); + 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_ex ("eax", src->static_label, src->size, src->is_unsigned); + } else { + emit_load_local_to_reg_ex ("eax", src->offset, src->size, src->is_unsigned); + } + + 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_ex ("eax", name, get_global_symbol_size (name), get_global_symbol_unsigned (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) { + + if (!state->ofp) { + return; + } + + switch (k) { + + case TOK_PLUS: case TOK_PLUSEQ: + + fprintf (state->ofp, " faddp\n"); + break; + + case TOK_MINUS: case TOK_MINUSEQ: + + fprintf (state->ofp, " fsubrp\n"); + break; + + case TOK_STAR: case TOK_STAREQ: + + fprintf (state->ofp, " fmulp\n"); + break; + + case TOK_BSLASH: case TOK_SLASHEQ: + + fprintf (state->ofp, " fdivrp\n"); + 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 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) { + + enum token_kind saved_calling_convention = postfix_member_calling_convention; + + 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 = scc_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 **) xrealloc (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) { + + scc_close (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); + } + + scc_close (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"); + + if (saved_calling_convention == TOK_STDCALL) { + fprintf (state->ofp, " add esp, %d\n", (DATA_PTR & 0x1f)); + } else { + 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"); + + if (saved_calling_convention == TOK_STDCALL) { + fprintf (state->ofp, " addl $%d, %%esp\n", (DATA_PTR & 0x1f)); + } else { + 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]) { + scc_close (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 int current_argument_is_bare_64bit_identifier_now (void) { + + struct local_symbol *sym; + + if (tok.kind != TOK_IDENT || !tok.ident || !current_argument_is_bare_identifier_now ()) { + return 0; + } + + sym = find_local_symbol (tok.ident); + + if (sym) { + return sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating; + } + + if (find_global_symbol (tok.ident) >= 0) { + return get_global_symbol_size (tok.ident) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (tok.ident); + } + + return 0; + +} + +static int current_argument_starts_64bit_integer_now (void) { + + if (tok.kind == TOK_CLLONG || tok.kind == TOK_CULLONG) { + return 1; + } + + return current_argument_is_bare_64bit_identifier_now (); + +} + +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_is_floating, ch, i; + int arg_bytes; + + int saved_arg_assignment32_stop_before_condition_operator; + int saved_arg_assignment64_stop_before_condition_operator; + + 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_saved_ofp = 0; + FILE *arg_tmp_ofp = 0; + FILE **arg_tmp_ofps = 0; + FILE **new_arg_tmp_ofps = 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 = scc_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); + } + + } + + } + + } + + saved_arg_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + saved_arg_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; + + assignment32_stop_before_condition_operator = 0; + assignment64_stop_before_condition_operator = 0; + + 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 = scc_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 () && !find_local_symbol (tok.ident) && 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 (!use_inline && get_global_symbol_has_prototype (name) && + argc < get_global_symbol_param_count (name) && + get_global_symbol_param_size (name, argc) == (DATA_LLONG & 0x1f) && + get_global_symbol_param_pointer_depth (name, argc) == 0 && + !get_global_symbol_param_floating (name, argc)) { + + arg_is_floating = 0; + arg_bytes = DATA_LLONG & 0x1f; + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", get_global_symbol_param_unsigned (name, argc)); + + } else if (!use_inline && get_global_symbol_has_prototype (name) && get_global_symbol_is_variadic (name) && argc >= get_global_symbol_param_count (name) && current_argument_starts_64bit_integer_now ()) { + + arg_is_floating = 0; + arg_bytes = DATA_LLONG & 0x1f; + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", rhs_current_operand_is_unsigned_now ()); + + } else 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 && arg_bytes == (DATA_LLONG & 0x1f)) { + + emit_push_reg_now ("edx"); + emit_push_reg_now ("eax"); + + } else 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 **) xrealloc (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) { + + scc_close (arg_tmp_ofp); + arg_tmp_ofp = 0; + + } + + argc++; + + if (!_accept (TOK_COMMA)) { + break; + } + + } + + } + + expect (TOK_RPAREN, ")"); + + assignment32_stop_before_condition_operator = saved_arg_assignment32_stop_before_condition_operator; + assignment64_stop_before_condition_operator = saved_arg_assignment64_stop_before_condition_operator; + + 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]) { + scc_close (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + + 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) { + + if (arg_tmp_ofps) { + + for (i = argc - 1; i >= 0; i--) { + + if (arg_tmp_ofps[i]) { + + fseek (arg_tmp_ofps[i], 0, SEEK_SET); + + while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { + fputc (ch, state->ofp); + } + + scc_close (arg_tmp_ofps[i]); + arg_tmp_ofps[i] = 0; + + } + + } + + 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 if (get_global_symbol_dllimport (name)) { + + remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); + fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " call dword [%s]\n" : " call dword ptr %s\n"), asm_global_import_symbol_name (name)); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (total_arg_bytes > 0 && get_global_symbol_calling_convention (name) != TOK_STDCALL) { + 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 if (get_global_symbol_dllimport (name)) { + + remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); + fprintf (state->ofp, " call *%s\n", asm_global_import_symbol_name (name)); + + } else { + fprintf (state->ofp, " call %s\n", asm_name); + } + + if (total_arg_bytes > 0 && get_global_symbol_calling_convention (name) != TOK_STDCALL) { + 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]) { + scc_close (arg_tmp_ofps[i]); + } + + } + + free (arg_tmp_ofps); + arg_tmp_ofps = 0; + + } + +} + +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 = 0; + +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; + + /* + * With a single fixed function frame, block-local symbols still have + * different logical stack depths, but ESP no longer changes when + * entering/leaving those blocks. The old trampoline fixup would + * therefore corrupt ESP before the real jump target. + */ + if (!current_function_uses_single_frame) { + + 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 = scc_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); + + scc_close (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) { + + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + + return 1; + + default: + + return 0; + + } + +} + +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) { + + 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 (current_integer_expr_is_foldable_now ()) { + + 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_statement_const32_to_edx (int64_s v); + +static int is_assignment32_condition_stop_operator (enum token_kind k) { + + /* + * Keep this as a switch rather than a chained || expression. This guard + * protects statement-condition parsing from consuming compare/logical + * operators too early; if a self-built stage miscompiles the || chain + * here, ordinary conditions can degrade into: + * + * mov eax, + * test eax, eax + */ + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + case TOK_LOGAND: + case TOK_LOGOR: + + return 1; + + default: + + return 0; + + } + +} + +static void emit_load_assignment_compare_expression_to_reg (const char *reg) { + + int64_s rhs_enum_value; + enum token_kind op; + + int lhs_pointer_depth; + int is_unsigned; + int rhs_is_enum; + + 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 (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { + return; + } + + if (is_value_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + rhs_is_enum = 0; + + if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &rhs_enum_value)) { + + rhs_is_enum = 1; + 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); + } + + } + + /* + * Keep identifier-vs-enum comparisons structurally as a real compare. + * During bootstrap this path is hit when compiling ordinary tests such + * as: + * + * unary_op == TOK_MINUS + * + * If the RHS enum is allowed to go through the generic expression loader, + * a later self-built compiler can collapse the expression into just: + * + * mov eax, 45 + * test eax, eax + * + * which makes every non-zero enum comparison true. Loading the enum + * directly into EDX avoids clobbering the already-loaded LHS in EAX and + * avoids the fragile push/pop/generic-RHS sequence entirely. + */ + if (rhs_is_enum) { + + emit_statement_const32_to_edx (rhs_enum_value); + + if (lhs_pointer_depth > 0) { + is_unsigned = 1; + } + + emit_compare_eax_edx_to_reg (op, reg, is_unsigned); + return; + + } + + emit_push_reg_now ("eax"); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + 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; + } + + /* + * C requires floating-to-integer conversion to discard the fractional + * part. x87 fistp uses the current FPU rounding mode, which is normally + * round-to-nearest, so values such as 0.5000000001 become 1 instead of 0. + * Temporarily switch the x87 control word to truncate for this conversion. + */ + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [esp + 4]\n" : " fnstcw word ptr [esp + 4]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [esp + 4]\n" : " mov ax, word ptr [esp + 4]\n"); + fprintf (state->ofp, " or ah, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [esp + 6], ax\n" : " mov word ptr [esp + 6], ax\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 6]\n" : " fldcw word ptr [esp + 6]\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 ? " fldcw word [esp + 4]\n" : " fldcw word ptr [esp + 4]\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, 8\n"); + + } else { + + fprintf (state->ofp, " subl $8, %%esp\n"); + fprintf (state->ofp, " fnstcw 4(%%esp)\n"); + fprintf (state->ofp, " movw 4(%%esp), %%ax\n"); + fprintf (state->ofp, " orb $12, %%ah\n"); + fprintf (state->ofp, " movw %%ax, 6(%%esp)\n"); + fprintf (state->ofp, " fldcw 6(%%esp)\n"); + fprintf (state->ofp, " fistpl (%%esp)\n"); + fprintf (state->ofp, " fldcw 4(%%esp)\n"); + fprintf (state->ofp, " movl (%%esp), %%%s\n", reg); + fprintf (state->ofp, " addl $8, %%esp\n"); + + } + +} + +static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi) { + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [esp + 8]\n" : " fnstcw word ptr [esp + 8]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [esp + 8]\n" : " mov ax, word ptr [esp + 8]\n"); + fprintf (state->ofp, " or ah, 12\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [esp + 10], ax\n" : " mov word ptr [esp + 10], ax\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 10]\n" : " fldcw word ptr [esp + 10]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp qword [esp]\n" : " fistp qword ptr [esp]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 8]\n" : " fldcw word ptr [esp + 8]\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", lo); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp + 4]\n" : " mov %s, dword ptr [esp + 4]\n", hi); + fprintf (state->ofp, " add esp, 12\n"); + + } else { + + fprintf (state->ofp, " subl $12, %%esp\n"); + fprintf (state->ofp, " fnstcw 8(%%esp)\n"); + fprintf (state->ofp, " movw 8(%%esp), %%ax\n"); + fprintf (state->ofp, " orb $12, %%ah\n"); + fprintf (state->ofp, " movw %%ax, 10(%%esp)\n"); + fprintf (state->ofp, " fldcw 10(%%esp)\n"); + fprintf (state->ofp, " fistpll (%%esp)\n"); + fprintf (state->ofp, " fldcw 8(%%esp)\n"); + fprintf (state->ofp, " movl (%%esp), %%%s\n", lo); + fprintf (state->ofp, " movl 4(%%esp), %%%s\n", hi); + fprintf (state->ofp, " addl $12, %%esp\n"); + + } + +} + +static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size) { + + if (!state->ofp) { + return; + } + + if ((size & 0x1f) == (DATA_LLONG & 0x1f)) { + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 8\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp], %s\n" : " mov dword ptr [esp], %s\n", lo); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp + 4], %s\n" : " mov dword ptr [esp + 4], %s\n", hi); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild qword [esp]\n" : " fild qword ptr [esp]\n"); + fprintf (state->ofp, " add esp, 8\n"); + + } else { + + fprintf (state->ofp, " subl $8, %%esp\n"); + fprintf (state->ofp, " movl %%%s, (%%esp)\n", lo); + fprintf (state->ofp, " movl %%%s, 4(%%esp)\n", hi); + fprintf (state->ofp, " fildll (%%esp)\n"); + fprintf (state->ofp, " addl $8, %%esp\n"); + + } + + return; + + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + + fprintf (state->ofp, " sub esp, 4\n"); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp], %s\n" : " mov dword ptr [esp], %s\n", lo); + fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild dword [esp]\n" : " fild dword ptr [esp]\n"); + fprintf (state->ofp, " add esp, 4\n"); + + } else { + + fprintf (state->ofp, " subl $4, %%esp\n"); + fprintf (state->ofp, " movl %%%s, (%%esp)\n", lo); + fprintf (state->ofp, " fildl (%%esp)\n"); + 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; + + if (rhs_current_operand_is_floating_now ()) { + + emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); + + /* + * Floating comparisons leave their boolean result in eax. + * Do not run that through fistp: emit_floating_compare_to_eax_now() + * has already consumed both x87 operands with fcompp, so the x87 + * stack no longer contains a value to convert. + */ + if (floating_rhs_result_in_eax_bool) { + + if (state->ofp && 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); + } + + } + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + emit_floating_stack_to_int_reg_now (reg); + return; + + } + + emit_load_assignment_compare_expression_to_reg (reg); + + for (;;) { + + if (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { + break; + } + + if (tok.kind == TOK_LOGAND) { + + false_label = anon_label++; + true_label = anon_label++; + end_label = anon_label++; + + get_token (); + + emit_test_reg_jump_zero_now (reg, false_label); + emit_load_assignment_compare_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); + + continue; + + } + + if (tok.kind == TOK_LOGOR) { + + false_label = anon_label++; + true_label = anon_label++; + end_label = anon_label++; + + get_token (); + + emit_test_reg_jump_nonzero_now (reg, true_label); + emit_load_assignment_compare_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); + + continue; + + } + + break; + + } + + 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_condition_stop_operator (enum token_kind k) { + + return 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 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 (current_integer_expr_is_foldable_now ()) { + + 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) && !(assignment64_stop_before_condition_operator && is_assignment64_condition_stop_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 (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { + + if (emit_load_prefix_incdec_member_to_reg_now ("eax")) { + + expect_semi_or_recover (); + return 1; + + } + + } + + 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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_first_array_count = declarator_first_array_count; + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + 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 lparen_expression_starts_with_star_now (void) { + + const char *p; + + if (tok.kind != TOK_LPAREN || !tok.caret) { + return 0; + } + + p = tok.caret + 1; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + return *p == '*'; + +} + +static int parse_parenthesized_indirect_assignment_statement (void) { + + enum token_kind op; + + int pointer_depth; + int pointed_size; + int deref_size = DATA_INT & 0x1f; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + get_token (); + emit_load_assignment_rhs_expression_to_reg ("edx"); + + pointer_depth = rhs_last_pointer_depth; + pointed_size = rhs_last_pointed_size; + + if (pointer_depth > 1) { + deref_size = DATA_PTR & 0x1f; + } else if (pointer_depth == 1 && pointed_size > 0) { + deref_size = pointed_size & 0x1f; + } + + if (deref_size <= 0) { + deref_size = DATA_INT & 0x1f; + } + + expect (TOK_RPAREN, ")"); + + if (!is_assignment_operator (tok.kind)) { + + 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 deref_size = DATA_INT & 0x1f; + int deref_is_floating = 0; + + enum token_kind op; + int global_index; + + 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 (); + } + + if (lparen_expression_starts_with_star_now ()) { + return parse_parenthesized_pointer_member_indirect_assignment_statement (); + } + + return parse_parenthesized_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; + deref_is_floating = 0; + + } 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; + deref_is_floating = lhs->pointed_is_floating; + + } + + } else { + + if (get_global_symbol_pointer_depth (name) > 1) { + + deref_size = DATA_PTR & 0x1f; + deref_is_floating = 0; + + } else if (get_global_symbol_pointer_depth (name) == 1) { + + deref_size = get_global_symbol_pointed_size (name); + deref_is_floating = get_global_symbol_pointed_is_floating (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_is_floating) { + + emit_push_reg_now ("edx"); + + emit_load_floating_rhs_expression_now (deref_size); + emit_pop_reg_now ("edx"); + + emit_store_floating_member_to_addr_reg_now ("edx", 0, deref_size); + expect_semi_or_recover (); + + free (name); + return 1; + + } + + 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) && !deref_is_floating) { + 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, src ? src->pointed_is_unsigned : get_global_symbol_pointed_is_unsigned (name)); + + 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 is_simple_assign; + 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; + int member_assignment_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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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 (is_simple_assign) { + + member_assignment_is_floating = member_is_floating || rhs_current_operand_is_floating_now (); + emit_push_reg_now ("edx"); + + if (member_assignment_is_floating) { + 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 (current_integer_expr_is_foldable_now ()) { + + 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_assignment_is_floating && is_simple_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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + get_token (); + + if (state->ofp) { + + if (is_simple_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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + get_token (); + + if (state->ofp) { + + if (is_simple_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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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 (is_simple_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) { + + enum token_kind member_calling_convention = last_found_member_calling_convention; + + 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 = scc_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 **) xrealloc (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) { + scc_close (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); + } + + scc_close (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"); + + if (member_calling_convention == TOK_STDCALL) { + fprintf (state->ofp, " add esp, %d\n", (DATA_PTR & 0x1f)); + } else { + 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"); + + if (member_calling_convention == TOK_STDCALL) { + fprintf (state->ofp, " addl $%d, %%esp\n", (DATA_PTR & 0x1f)); + } else { + 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; + + is_simple_assign = (tok.kind == TOK_ASSIGN); + 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 (is_simple_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 (is_simple_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 (is_simple_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 (is_simple_assign) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + if (lhs) { + + if (lhs->is_static && lhs->static_label) { + emit_load_global_to_reg_ex ("eax", lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex ("eax", lhs->offset, lhs->size, lhs->is_unsigned); + } + + } 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_CCHAR || k == TOK_LCHAR || k == TOK_CINT || k == TOK_CUINT || k == TOK_CLONG || k == TOK_CULONG || k == TOK_CLLONG || k == TOK_CULLONG; +} + +static int token_is_integer_unsigned_constant_now (enum token_kind k) { + return k == TOK_CUINT || k == TOK_CULONG || k == TOK_CULLONG; +} + +#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 void emit_statement_const32_to_edx (int64_s v) { + + flush_pending_statement_labels (); + + if (!state->ofp) { + return; + } + + if (state->syntax & ASM_SYNTAX_INTEL) { + fprintf (state->ofp, " mov edx, %lu\n", v.low & U32_MASK); + } else { + fprintf (state->ofp, " movl $%lu, %%edx\n", v.low & U32_MASK); + } + +} + +static int emit_statement_rhs_const32_to_edx_if_possible (void) { + + int64_s v; + + if (token_is_const_condition_operand_now ()) { + + if (current_integer_expr_is_foldable_now ()) { + v = const64_from_current_foldable_expr (); + } else { + v = const64_from_current_operand (); + } + + emit_statement_const32_to_edx (v); + return 1; + + } + + if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &v)) { + + get_token (); + + emit_statement_const32_to_edx (v); + return 1; + + } + + return 0; + +} + +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; + } + + /* + * Parenthesized floating conditions such as: + * + * if (num < 0) + * while (b >= 10.0) + * + * arrive here while TOK_LPAREN is still current. If we don't recognise + * the name inside the parens as floating, the generic parenthesized + * integer path consumes the expression and compares only the low dword. + */ + if (find_local_symbol (name)) { + + struct local_symbol *src = find_local_symbol (name); + return src && src->is_floating ? 1 : 0; + + } + + if (find_global_symbol (name) >= 0) { + return get_global_symbol_floating (name) ? 1 : 0; + } + + return 0; + +} + +static int source_va_arg_type_is_double_now (const char *p) { + + int paren; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + paren = 0; + + while (*p) { + + if (*p == '(') { + paren++; + } else if (*p == ')') { + + paren--; + + if (paren == 0) { + return 0; + } + + } else if (paren == 1 && *p == ',') { + + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (strncmp (p, "double", 6) == 0 && + !(p[6] == '_' || (p[6] >= '0' && p[6] <= '9') || + (p[6] >= 'A' && p[6] <= 'Z') || + (p[6] >= 'a' && p[6] <= 'z'))) { + return 1; + } + + } + + p++; + + } + + return 0; + +} + +static int rhs_current_operand_is_floating_now (void) { + + if (token_is_floating_constant_now ()) { + return 1; + } + + if (tok.kind == TOK_SCC_BUILTIN_VA_ARG) { + + const char *p = tok.caret ? tok.caret + tok.len : 0; + + if (source_va_arg_type_is_double_now (p)) { + 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 (strcmp (tok.ident, "__scc_builtin_va_arg") == 0 && source_va_arg_type_is_double_now (p)) { + return 1; + } + + if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) { + return get_global_function_returns_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_function_returns_floating (tok.ident) ? 1 : 0; + } + + } + + return 0; + +} + +static int64_s floating_ld_to_bits_now (int size, 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, 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) { + + switch (k) { + + case TOK_LESS: + case TOK_LTEQ: + case TOK_GREATER: + case TOK_GTEQ: + case TOK_EQEQ: + case TOK_NOTEQ: + + return 1; + + default: + + return 0; + + } + +} + +static void emit_statement_cmp_eax_edx_jump_if_false (enum token_kind op, int is_unsigned, int label); +static int statement_condition_emit_logical_tail (int label); + +static void emit_statement_jump_if_false (int label); +static int emit_statement_cmp_eax_edx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label); + +static int source_condition_tail_end_now (const char *p) { + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + switch (*p) { + + case '\0': + case ')': + case ';': + case '{': + + return 1; + + default: + + break; + + } + + return 0; + +} + +static int source_condition_skip_rhs_const_or_enum_now (const char **pp) { + + char word[128]; + + const char *p; + int i; + + int64_s ignored; + + if (!pp || !*pp) { + return 0; + } + + p = *pp; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + i = 0; + + while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || + (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && + i + 1 < (int) sizeof (word)) { + i++; + } + + memcpy (word, p, (unsigned long) i); + word[i] = 0; + + if (!resolve_enum_constant (word, &ignored)) { + return 0; + } + + p += i; + + *pp = p; + return 1; + + } + + if (*p == '\'') { + + p++; + + while (*p && *p != '\'') { + + if (*p == '\\' && p[1]) { + p += 2; + } else { + p++; + } + + } + + if (*p != '\'') { + return 0; + } + + p++; + + *pp = p; + return 1; + + } + + if (*p == '-' || *p == '+') { + p++; + } + + if (*p < '0' || *p > '9') { + return 0; + } + + while ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'F') || + (*p >= 'a' && *p <= 'f') || + *p == 'x' || *p == 'X' || + *p == 'u' || *p == 'U' || + *p == 'l' || *p == 'L') { + p++; + } + + *pp = p; + return 1; + +} + +static int emit_statement_rhs_const32_or_enum_to_edx_if_possible (void) { + + int64_s v; + + if (tok.kind == TOK_IDENT && tok.ident && resolve_enum_constant (tok.ident, &v)) { + + get_token (); + + emit_statement_const32_to_edx (v); + return 1; + + } + + if (token_is_const_condition_operand_now ()) { + + if (current_integer_expr_is_foldable_now ()) { + v = const64_from_current_foldable_expr (); + } else { + v = const64_from_current_operand (); + } + + emit_statement_const32_to_edx (v); + return 1; + + } + + return 0; + +} + +static int source_condition_logical_rhs_is_enum_compare_now (const char *p) { + + if (!p) { + return 0; + } + + for (;;) { + + if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) { + p += 2; + } else { + return 0; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(') { + return 0; + } + + for (;;) { + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + break; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (source_condition_tail_end_now (p)) { + return 1; + } + + if (!((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|'))) { + return 0; + } + + } + +} + +static const char *source_find_current_ident_on_line (const char *p) { + + int name_len; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + name_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) name_len) == 0 && + !ident_char_now ((unsigned char) p[name_len])) { + return p + name_len; + } + + p++; + + } + + return 0; + +} + +static int source_condition_ident_enum_compare_now (const char *p) { + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + /* + * tok.start is usually the beginning of the source line, not the current + * identifier. When compiling the compiler itself this made simple enum + * tests such as: + * + * if (assign_op == TOK_ASSIGN) + * + * miss the fixed compare-emission path and fall into the generic + * expression folder, where later bootstrap stages reduced the condition + * to "mov eax, TOK_ASSIGN; test eax, eax". Locate the current token on + * the line first, then inspect the text that follows it. + */ + p = source_find_current_ident_on_line (p); + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(' || *p == '.' || (p[0] == '-' && p[1] == '>')) { + return 0; + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + /* + * This fast path emits a branch-form compare, which is exactly what is + * needed for logical tails. Do not send enum-token compares through the + * generic expression path: later self-built stages have repeatedly reduced + * them to "mov eax, ; test eax,eax". + */ + if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { + return source_condition_logical_rhs_is_enum_compare_now (p); + } + + return source_condition_tail_end_now (p); + +} + +static int source_condition_ident_immediate_compare_now (const char *p) { + + int name_len; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + name_len = (int) strlen (tok.ident); + p = source_find_current_ident_on_line (p); + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + /* + * Only catch immediate primary/member comparisons here. Do not claim + * calls, subscripts, or arithmetic expressions; those still need the + * ordinary expression parser. + */ + if (*p == '(' || *p == '[') { + return 0; + } + + for (;;) { + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + break; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + } + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + p++; + } else { + + (void) name_len; + return 0; + + } + + /* + * This shortcut emits one cmp/jcc and returns to the statement parser. + * It must only claim a whole simple condition. If the comparison is + * followed by a top-level logical operator, for example: + * + * m->nargs >= 0 || m->is_variadic + * ch >= 'a' && ch <= 'f' + * + * then claiming only the first comparison leaves the ||/&& tail to a + * parser path that expects an integer constant expression. Let the + * ordinary expression parser own those full logical conditions instead. + */ + { + + int paren_depth = 0; + int bracket_depth = 0; + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + continue; + + } + + if (*p == '(') { + + paren_depth++; + p++; + + continue; + + } + + if (*p == ')') { + + if (paren_depth == 0) { + break; + } + + paren_depth--; + p++; + + continue; + + } + + if (*p == '[') { + + bracket_depth++; + p++; + + continue; + + } + + if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + p++; + continue; + + } + + if (paren_depth == 0 && bracket_depth == 0 && + ((p[0] == '&' && p[1] == '&') || + (p[0] == '|' && p[1] == '|'))) { + return 0; + } + + p++; + + } + + } + + (void) name_len; + return 1; + +} + +static int emit_statement_ident_immediate_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_ident_immediate_compare_now (tok.caret) && + !source_condition_ident_immediate_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("eax"); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now ("eax"); + } + + if (postfix_member_seen && postfix_member_is_unsigned) { + is_unsigned = 1; + } + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int emit_statement_ident_enum_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_ident_enum_compare_now (tok.caret) && !source_condition_ident_enum_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("eax"); + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + /* + * This path is specifically an EAX-vs-constant statement comparison. + * Do not route the RHS through the generic "load constant to named + * register" helper here. During bootstrap the named-register helper path + * is fragile: if the next-stage compiler mis-parses the string argument or + * folds the call badly, enum compares such as: + * + * if (unary_op == TOK_MINUS) + * + * can degrade into: + * + * mov eax, TOK_MINUS + * test eax, eax + * + * which makes every non-zero enum condition true. Emit the RHS into EDX + * directly so the following cmp/jcc sequence is structurally fixed. + */ + if (!emit_statement_rhs_const32_or_enum_to_edx_if_possible ()) { + return 0; + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int source_condition_member_enum_compare_now (const char *p) { + + char word[128]; + int i; + + if (tok.kind != TOK_IDENT || !tok.ident || !p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (strncmp (p, tok.ident, (unsigned long) strlen (tok.ident)) == 0 && !ident_char_now ((unsigned char) p[strlen (tok.ident)])) { + p += strlen (tok.ident); + } else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { + + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { + p++; + } + + } else if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { + return 0; + } + + for (;;) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '.') { + p++; + } else if (p[0] == '-' && p[1] == '>') { + p += 2; + } else { + return 0; + } + + 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 == '_') { + p++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { + break; + } + + } + + if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { + p += 2; + } else if (*p == '<' || *p == '>') { + p++; + } else { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (!source_condition_skip_rhs_const_or_enum_now (&p)) { + return 0; + } + + i = 0; + + while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && i + 1 < (int) sizeof (word)) { + i++; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { + return source_condition_logical_rhs_is_enum_compare_now (p); + } + + return source_condition_tail_end_now (p); + +} + +static int emit_statement_member_enum_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (!source_condition_member_enum_compare_now (tok.caret) && !source_condition_member_enum_compare_now (tok.start)) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_to_reg ("eax"); + + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { + emit_apply_postfix_member_access_to_reg_now ("eax"); + } + + if (postfix_member_seen && postfix_member_is_unsigned) { + is_unsigned = 1; + } + + if (!token_is_statement_compare_operator (tok.kind)) { + return 0; + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_or_enum_to_edx_if_possible ()) { + return 0; + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +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 (double left, enum token_kind op, 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.0; + + } + +} + +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 void emit_statement_test_pair_jump_if_true (const char *lo, const char *hi, int label) { + + if (!state->ofp) { + return; + } + + 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"), label); + fprintf (state->ofp, " test %s, %s\n", lo, lo); + 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", hi, hi); + fprintf (state->ofp, " jnz .L%d\n", label); + fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo); + fprintf (state->ofp, " jnz .L%d\n", label); + + } + +} + +static int statement_condition_constant_known = 0; +static int statement_condition_constant_value = 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_ex ("eax", lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex ("eax", lhs->offset, lhs->size, lhs->is_unsigned); + } + + } 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 = scc_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); + scc_close (step_tmp); + + step_tmp = 0; + + } + + emit_statement_jump (loop_label); + + } + + if (step_tmp) { + scc_close (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 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 void emit_statement_eax_truth_jump_if_false_and_tail (int label) { + + int skip_label; + + if (tok.kind == TOK_LOGAND) { + + emit_statement_test_eax_jump_if_false (label); + + get_token (); + emit_statement_jump_if_false (label); + + return; + + } + + if (tok.kind == TOK_LOGOR) { + + skip_label = anon_label++; + emit_test_reg_jump_nonzero_now ("eax", skip_label); + get_token (); + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + return; + + } + + emit_statement_test_eax_jump_if_false (label); + +} + +static int emit_statement_cmp_eax_edx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label) { + + int rhs_label; + int skip_label; + + if (tok.kind == TOK_LOGAND) { + + get_token (); + + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + emit_statement_jump_if_false (label); + + return 1; + + } + + if (tok.kind == TOK_LOGOR) { + + rhs_label = anon_label++; + skip_label = anon_label++; + + get_token (); + + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, rhs_label); + emit_statement_jump (skip_label); + + emit_statement_label (rhs_label); + emit_statement_jump_if_false (label); + + emit_statement_label (skip_label); + return 1; + + } + + emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); + return 1; + +} + +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; + } + + /* + * Only claim a single parenthesized assignment here. Do not treat + * a parenthesized logical group whose first term is an assignment as + * one big assignment expression. For example: + * + * ((op2 = get_op (pp)) || (brackets && **pp == ')')) + * + * The old scanner consumed both leading '(' characters, parsed only + * the first assignment, and then tried to manage the outer group's + * closing ')' with parenthesized_assignment_open_parens. That left + * the statement-condition parser out of sync at the final ')' before + * the following '{'. + * + * A real parenthesized-assignment fast path starts with exactly one + * '(' at the current token. Nested/grouped logical expressions must + * be left to the normal expression parser. + */ + parens = 1; + p++; + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '(') { + return 0; + } + + 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++; + } + + 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 (current_integer_expr_is_foldable_now ()) { + + 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_ex (reg, lhs->static_label, lhs->size, lhs->is_unsigned); + } else { + emit_load_local_to_reg_ex (reg, lhs->offset, lhs->size, lhs->is_unsigned); + } + + } 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 int source_condition_has_top_level_compare_now (const char *p) { + + int paren_depth = 0; + int bracket_depth = 0; + int saw_operand = 0; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + saw_operand = 1; + continue; + + } + + if (*p == '(') { + + paren_depth++; + + saw_operand = 1; + p++; + + continue; + + } + + if (*p == ')') { + + if (paren_depth == 0) { + return 0; + } + + paren_depth--; + saw_operand = 1; + + p++; + continue; + + } + + if (*p == '[') { + + bracket_depth++; + + saw_operand = 1; + p++; + + continue; + + } + + if (*p == ']') { + + if (bracket_depth > 0) { + bracket_depth--; + } + + saw_operand = 1; + p++; + + continue; + + } + + if (paren_depth == 0 && bracket_depth == 0) { + + if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|') || + *p == '?' || *p == ':' || *p == ',' || *p == ';') { + return 0; + } + + if (saw_operand) { + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + return 1; + } + + if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + return 1; + } + + } + + } + + if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + saw_operand = 1; + } + + p++; + + } + + return 0; + +} + +static int source_parenthesized_lhs_followed_by_compare_now (const char *p) { + + int paren_depth = 0; + + if (!p) { + return 0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p != '(') { + return 0; + } + + while (*p) { + + if (*p == '\'' || *p == '"') { + + int quote = *p++; + + while (*p) { + + if (*p == '\\' && p[1]) { + + p += 2; + continue; + + } + + if (*p == quote) { + + p++; + break; + + } + + p++; + + } + + continue; + + } + + if (*p == '(') { + + paren_depth++; + p++; + + continue; + + } + + if (*p == ')') { + + paren_depth--; + p++; + + if (paren_depth == 0) { + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if ((p[0] == '=' && p[1] == '=') || + (p[0] == '!' && p[1] == '=') || + (p[0] == '<' && p[1] == '=') || + (p[0] == '>' && p[1] == '=')) { + return 1; + } + + if ((p[0] == '<' && p[1] != '<') || + (p[0] == '>' && p[1] != '>')) { + return 1; + } + + return 0; + + } + + if (paren_depth < 0) { + return 0; + } + + continue; + + } + + p++; + + } + + return 0; + +} + +static int emit_statement_parenthesized_lhs_compare_jump_if_false_now (int label) { + + enum token_kind op; + int is_unsigned; + + if (tok.kind != TOK_LPAREN) { + return 0; + } + + if (!source_parenthesized_lhs_followed_by_compare_now (tok.caret)) { + return 0; + } + + get_token (); + + is_unsigned = rhs_current_operand_is_unsigned_now (); + emit_load_assignment_rhs_expression_to_reg ("eax"); + + expect (TOK_RPAREN, ")"); + + if (!token_is_statement_compare_operator (tok.kind)) { + + emit_statement_eax_truth_jump_if_false_and_tail (label); + return 1; + + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static int emit_statement_direct_compare_jump_if_false_now (int label) { + + enum token_kind op; + + int is_unsigned; + int old_assignment32_stop_before_condition_operator; + + if (!source_condition_has_top_level_compare_now (tok.caret) && + !source_condition_has_top_level_compare_now (tok.start)) { + return 0; + } + + /* + * Do not let the generic integer direct-compare fast path consume + * floating conditions such as: + * + * while (value_fractional_d - ((int64_t)value_fractional_d) != 0) + * + * That path lowers the LHS through eax and compares integer dwords, + * so the example above becomes low_word - low_word and the fractional + * loop exits immediately. Leave floating expressions for the x87 + * condition path below. + */ + if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) { + return 0; + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + assignment32_stop_before_condition_operator = 1; + + if (emit_load_assignment_binary_expression_prec_to_reg ("eax", 1)) { + is_unsigned = 1; + } + + assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; + + if (!token_is_statement_compare_operator (tok.kind)) { + + emit_statement_eax_truth_jump_if_false_and_tail (label); + return 1; + + } + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return 1; + +} + +static void emit_statement_jump_if_false (int label) { + + enum token_kind op; + int is_unsigned; + + const char *call_start; + const char *call_caret; + + char *call_name; + unsigned long call_line; + + int old_assignment32_stop_before_condition_operator; + flush_pending_statement_labels (); + + statement_condition_constant_known = 0; + statement_condition_constant_value = 0; + + if (emit_statement_ident_immediate_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_ident_enum_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_member_enum_compare_jump_if_false_now (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_compare_expression_to_reg ("edx"); + consume_parenthesized_assignment_remaining_closes (); + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + statement_condition_emit_logical_tail (label); + + return; + + } + + consume_parenthesized_assignment_remaining_closes (); + + emit_statement_eax_truth_jump_if_false_and_tail (label); + return; + + } + + if (emit_statement_parenthesized_lhs_compare_jump_if_false_now (label)) { + return; + } + + if (emit_statement_direct_compare_jump_if_false_now (label)) { + return; + } + + if (tok.kind == TOK_LPAREN && (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ())) { + + double left_float = 0.0; + + 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 ())) { + + 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 = int64_to_double_now (right_int); + + } + + 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 (floating_rhs_result_in_eax_bool) { + + emit_statement_eax_truth_jump_if_false_and_tail (label); + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + if (left_float_constant) { + + statement_condition_constant_known = 1; + statement_condition_constant_value = left_float != 0.0 ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + emit_load_floating_ld_now (float_size, 0.0); + + emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); + return; + + } + + /* + * Parenthesized statement conditions must be parsed as ordinary + * expressions here. The older recursive/special-case paths try to split + * things like: + * + * (p - q) > 2 + * ((*u++ = *t++) != '\n') + * (toupper((unsigned char)specifier) == 'X') + * + * into a hand-emitted branch form. That interacts badly with the + * stop-before-condition-operator guard and makes the inner expression + * parser treat non-constant subexpressions as integer constant + * expressions. Let the normal expression parser consume the whole + * parenthesized expression, then branch on the resulting value. If the + * parenthesized value is followed by a comparison, handle that comparison + * normally after the grouped LHS has been loaded. + */ + if (tok.kind == TOK_LPAREN) { + + 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; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_eax_truth_jump_if_false_and_tail (label); + return; + + } + + if (tok.kind == TOK_LPAREN && !source_starts_with_parenthesized_assignment_now ()) { + + get_token (); + + emit_statement_jump_if_false (label); + expect (TOK_RPAREN, ")"); + + statement_condition_emit_logical_tail (label); + return; + } + + if (statement_condition_starts_with_ident_call_now ()) { + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + if (tok.kind != TOK_IDENT) { + emit_load_assignment_rhs_expression_to_reg ("eax"); + } else { + + call_name = xstrdup (tok.ident); + call_start = tok.start; + call_caret = tok.caret; + call_line = get_line_number (); + + get_token (); + + 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, "eax", call_start, call_caret, call_line); + free (call_name); + + } + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_eax_truth_jump_if_false_and_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 ()) { + + double left_float = 0.0; + + 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 ())) { + + 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 = int64_to_double_now (right_int); + + } + + 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 (floating_rhs_result_in_eax_bool) { + + emit_statement_eax_truth_jump_if_false_and_tail (label); + + floating_rhs_result_in_eax_bool = 0; + return; + + } + + if (left_float_constant) { + + statement_condition_constant_known = 1; + statement_condition_constant_value = left_float != 0.0 ? 1 : 0; + + if (statement_condition_fold_logical_tail (label)) { + return; + } + + return; + + } + + emit_load_floating_ld_now (float_size, 0.0); + emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); + + return; + + } + + /* + * Only take the statement-condition constant shortcut when the current + * token itself is a constant operand. The old text-based fallbacks could + * misfire during self-compilation of enum comparisons such as: + * + * if (assign_op == TOK_ASSIGN) + * + * and reduce the whole condition to the RHS enum value. That generated + * "mov eax, 61; test eax, eax" and made every compound assignment look + * like a plain assignment in the next bootstrap stage. + */ + if (token_is_const_condition_operand_now ()) { + + int fold_whole_expr = current_integer_expr_is_foldable_now (); + 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_statement_cmp_eax_edx_jump_if_false_and_tail (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 ()) { + + double left_float; + double right_float; + + left_float = int64_to_double_now (left); + 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 (current_integer_expr_is_foldable_now ()) { + 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 ()) { + + double left_float = int64_to_double_now (left); + + 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_and_tail (op, is_unsigned, 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_eax_truth_jump_if_false_and_tail (label); + return; + + } + + if (current_expression_mentions_64bit_symbol_now ()) { + + int old_assignment64_stop_before_condition_operator; + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; + assignment64_stop_before_condition_operator = 1; + + emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); + assignment64_stop_before_condition_operator = old_assignment64_stop_before_condition_operator; + + 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"); + + } + + } + + /* + * The stop-before-compare guard is only needed while loading the + * left hand side of the statement condition. After the condition + * operator has been consumed, the right hand side must be parsed as + * a complete expression. Keeping the guard enabled here makes a + * parenthesized conditional expression such as: + * + * i < (len_fract == 0 ? 1 : precision - len_fract) + * + * stop at the nested == and leave the parser expecting the closing + * ')'. That is what produced the false "expected )" diagnostic. + */ + 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; + + } + + if (tok.kind == TOK_LOGAND) { + + get_token (); + + emit_statement_test_pair_jump_if_false ("eax", "edx", label); + emit_statement_jump_if_false (label); + + return; + + } + + if (tok.kind == TOK_LOGOR) { + + int skip_label = anon_label++; + get_token (); + + emit_statement_test_pair_jump_if_true ("eax", "edx", skip_label); + emit_statement_jump_if_false (label); + emit_statement_label (skip_label); + + return; + + } + + emit_statement_test_pair_jump_if_false ("eax", "edx", label); + return; + + } + + is_unsigned = rhs_current_operand_is_unsigned_now (); + + old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; + assignment32_stop_before_condition_operator = 1; + + emit_load_assignment_rhs_expression_to_reg ("eax"); + assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; + + if (token_is_statement_compare_operator (tok.kind)) { + + op = tok.kind; + get_token (); + + if (rhs_current_operand_is_unsigned_now ()) { + is_unsigned = 1; + } + + if (!emit_statement_rhs_const32_to_edx_if_possible ()) { + + emit_push_reg_now ("eax"); + emit_load_assignment_compare_expression_to_reg ("edx"); + emit_pop_reg_now ("eax"); + + } + + emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); + return; + + } + + emit_statement_eax_truth_jump_if_false_and_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 (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_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 = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_tag_name (name) : 0; + + } + + } else if (tok.kind == TOK_DOT) { + + if (src) { + + base_size = src->size; + base_tag_name = src->tag_name; + + } else { + + base_size = get_global_symbol_size (name); + base_tag_name = get_global_symbol_tag_name (name); + + } + + } 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) { + + int target_label = current_break_label; + + long cleanup_base = current_break_cleanup_base; + long cleanup_bytes; + + get_token (); + + if (target_label >= 0) { + + cleanup_bytes = current_block_cleanup_bytes - cleanup_base; + + if (!current_function_uses_single_frame && 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_CONTINUE) { + + int target_label = current_continue_label; + + long cleanup_base = current_continue_cleanup_base; + long cleanup_bytes; + + get_token (); + + if (target_label >= 0) { + + cleanup_bytes = current_block_cleanup_bytes - cleanup_base; + + if (!current_function_uses_single_frame && 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 *function_tmp = 0; + + char *inline_asm_text = 0; + char *function_asm_text = 0; + + int capture_inline_body = 0; + int capture_function_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; + capture_function_body = saved_ofp != 0; + + if (capture_function_body) { + + function_tmp = scc_tmpfile (); + + if (!function_tmp) { + capture_function_body = 0; + } + + } + + if (emit_body) { + + if (parsed_dllimport) { + report_line_at (get_filename (), last_declarator_name_line, REPORT_ERROR, last_declarator_name_start, last_declarator_name_caret, "function '%s' cannot be defined with '__dllimport'", name); + } + + 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_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, 0); + 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_calling_convention = (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention; + current_function_param_stack_bytes = 0; + current_function_has_return_statement = 0; + + parse_old_style_param_decls (); + remove_duplicate_pending_params (); + + /* + * Old-style definitions start with an identifier list, so the first + * function-symbol install only knows the default int-sized parameter + * placeholders. The declaration list between ')' and '{' is what gives + * those parameters their real types. Refresh the saved call signature + * after that list has been parsed, otherwise later calls in the same + * translation unit pass 64-bit parameters as single words. + */ + if (emit_body && should_emit) { + copy_pending_params_to_global_symbol (name); + } + + current_function_param_stack_bytes = global_symbol_stdcall_stack_bytes (name); + + 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_function_body) { + state->ofp = function_tmp; + } + + current_return_label = anon_label++; + pending_return_jump = 0; + current_function_preserve_assignment64_regs = 0; + + /* + * Do not apply the whole-function frame deferral to inline-function + * capture. The inline expander analyses the captured body as it was + * emitted, including any stack movement before parameter references. + * Moving all automatic storage to a synthetic prologue changes that + * analysis and can corrupt the self-built compiler. + */ + current_function_frame_enabled = (!is_inline && capture_function_body); + emit_function_start (name, !emit_public); + + 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 (); + + current_function_frame_label = -1; + current_function_frame_deferred = 0; + current_function_frame_enabled = 0; + + emit_goto_trampolines (); + current_function_uses_single_frame = 0; + + if (capture_function_body && function_tmp) { + + function_asm_text = read_tmp_file_text (function_tmp); + + if (function_asm_text) { + + inline_asm_text = replace_function_frame_placeholder (function_asm_text, current_function_frame_size); + + if (!inline_asm_text) { + + inline_asm_text = function_asm_text; + function_asm_text = 0; + + } + + if (!capture_inline_body || emit_inline_definition_to_output) { + fputs (inline_asm_text, saved_ofp); + } + + if (capture_inline_body) { + + 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); + + } + + } + + if (function_asm_text) { + free (function_asm_text); + } + + if (inline_asm_text) { + + free (inline_asm_text); + inline_asm_text = 0; + + } + + scc_close (function_tmp); + function_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; + + } + + clear_pending_params (); + + declarator_function_param_count = 0; + declarator_function_has_prototype = 0; + declarator_function_is_variadic = 0; + + if (tok.kind != TOK_RPAREN) { + + int old_style_param_start = pending_param_count; + + for (;;) { + + if (tok.kind == TOK_ELLIPSIS) { + + declarator_function_is_variadic = 1; + declarator_function_has_prototype = 1; + + get_token (); + + } else if (is_type_start (tok.kind)) { + + int param_base_size; + int param_size; + int param_pointer_depth; + int saved_function_param_count; + + int count_this_param = 0; + int saw_void_param_list = 0; + + char *param_name = 0; + + parse_type_spec (); + param_base_size = parsed_type_size; + + preserve_pending_params++; + + 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 (); + } + + saved_function_param_count = declarator_function_param_count; + + if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) { + parse_declarator (¶m_name); + } + + preserve_pending_params--; + + 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); + param_pointer_depth = declarator_pointer_depth; + + if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { + param_pointer_depth = 1; + } + + if (param_name && find_pending_param_from (param_name, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", param_name); + } else { + + add_pending_param (param_name, param_size, type_alignment (param_size), + (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), + param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); + + } + + } else { + saw_void_param_list = 1; + } + + if (param_name) { + free (param_name); + } + + declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0); + declarator_function_has_prototype = declarator_function_has_prototype || count_this_param || saw_void_param_list; + + } else if (token_is_ident ()) { + + if (find_pending_param_from (tok.ident, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", tok.ident); + } else { + + add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0, 0); + declarator_function_param_count++; + + } + + 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); + + if (parsed_dllexport || declarator_dllexport) { + + vec_push (&vec_dllexports, xstrdup (name)); + + declarator_dllexport = 0; + parsed_dllexport = 0; + + } + + free (name); + return 1; + + } + + free (name); + return 0; + +} + +static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) { + append_global_init_value (values, max_values, count, value, DATA_CHAR & 0x1f); +} + +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 char masm_pending_data_label[512]; +static char masm_open_data_directive[8]; + +static int masm_has_pending_data_label = 0; +static int masm_data_line_open = 0; +static int masm_data_line_values = 0; + +static void masm_set_pending_data_label (const char *name) { + + const char *asm_name = asm_global_symbol_name (name); + unsigned long len; + + if (!asm_name) { + + masm_has_pending_data_label = 0; + masm_pending_data_label[0] = 0; + + return; + + } + + len = strlen (asm_name); + + if (len >= sizeof (masm_pending_data_label)) { + len = sizeof (masm_pending_data_label) - 1; + } + + memcpy (masm_pending_data_label, asm_name, len); + + masm_pending_data_label[len] = 0; + masm_has_pending_data_label = 1; + +} + +static void masm_flush_data_line (void) { + + if (masm_data_line_open) { + + fprintf (state->ofp, "\n"); + + masm_data_line_open = 0; + masm_data_line_values = 0; + + masm_open_data_directive[0] = 0; + + } + +} + +static void masm_emit_data_prefix (const char *directive) { + + if (masm_has_pending_data_label) { + + masm_flush_data_line (); + fprintf (state->ofp, "%s %s ", masm_pending_data_label, directive); + + masm_has_pending_data_label = 0; + masm_pending_data_label[0] = 0; + + strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); + + masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; + masm_data_line_open = 1; + masm_data_line_values = 0; + + } else if (masm_data_line_open && strcmp (masm_open_data_directive, directive) == 0) { + + if (masm_data_line_values >= 8) { + + fprintf (state->ofp, "\n %s ", directive); + masm_data_line_values = 0; + + } else { + fprintf (state->ofp, ", "); + } + + } else { + + masm_flush_data_line (); + + fprintf (state->ofp, " %s ", directive); + strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); + + masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; + masm_data_line_open = 1; + masm_data_line_values = 0; + + } + + masm_data_line_values++; + +} + +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) { + + if (size == (DATA_CHAR & 0x1f)) { + + masm_emit_data_prefix ("db"); + fprintf (state->ofp, "%ld", low & 0xFFUL); + + } else if (size == (DATA_SHORT & 0x1f)) { + + masm_emit_data_prefix ("dw"); + fprintf (state->ofp, "%ld", low & 0xFFFFUL); + + } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { + + masm_emit_data_prefix ("dq"); + fprintf (state->ofp, "0%08lX%08lXh", high & U32_MASK, low & U32_MASK); + + } else { + + masm_emit_data_prefix ("dd"); + fprintf (state->ofp, "%lu", low & U32_MASK); + + } + + } else if (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) { + + masm_emit_data_prefix ("dd"); + fprintf (state->ofp, "offset %s", 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) { + + masm_emit_data_prefix ("db"); + fprintf (state->ofp, "%ld dup (?)", 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_data_label (const char *name, int is_static) { + + const char *asm_name = asm_global_symbol_name (name); + + if (state->syntax & ASM_SYNTAX_MASM) { + + masm_flush_data_line (); + + if (!is_static) { + fprintf (state->ofp, "public %s\n", asm_name); + } + + masm_set_pending_data_label (name); + + } else { + emit_global_label (name, is_static); + } + +} + +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_data_label (name, is_static); + emit_global_space (total); + + return; + + } + + switch_section (SECTION_DATA); + emit_global_data_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]; + char skip_label[64]; + + int elem_size = DATA_CHAR & 0x1f; + int value_count = 0, i; + + if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { + sprintf (label, "LC%d", anon_label++); + } else { + sprintf (label, ".LC%d", anon_label++); + } + + parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count); + elem_size = parsed_string_initializer_elem_size; + + /* + * 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) { + + 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_data_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], elem_size); + } + + switch_section (SECTION_TEXT); + emit_global_label (skip_label, 1); + + return xstrdup (label); + + } + + switch_section (SECTION_DATA); + emit_global_data_label (label, 1); + + for (i = 0; i < value_count; i++) { + emit_global_scalar (values[i], elem_size); + } + + 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 = get_global_symbol_dllimport (name) ? asm_global_import_symbol_name (name) : asm_global_symbol_name (name); + + if (get_global_symbol_dllimport (name)) { + + size = DATA_PTR & 0x1f; + is_function = 0; + + } + + 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 declaration_dllimport = 0; + + 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_first_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; + declaration_dllimport = parsed_dllimport; + + parse_declarator (&name); + + declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; + 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_first_array_count = declarator_first_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, + (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, + 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 && declarator_has_function && (tok.kind == TOK_LBRACE || is_type_start (tok.kind))) { + + if (parsed_dllexport || declarator_dllexport) { + + vec_push (&vec_dllexports, xstrdup (name)); + + declarator_dllexport = 0; + parsed_dllexport = 0; + + } + + 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_first_array_count = decl_first_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. + */ + add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + 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); + + set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); + set_global_symbol_dllimport (name, declaration_dllimport); + + copy_pending_params_to_global_symbol (name); + + } + + } else if ((declaration_storage == STORAGE_EXTERN || declaration_dllimport) && init_value_count == 0) { + + add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); + + if (find_global_symbol (name) >= 0) { + + 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + set_global_symbol_dllimport (name, declaration_dllimport); + + } + + } 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); + set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); + + 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_unit32 (void) { + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + + fprintf (state->ofp, ".386\n"); + fprintf (state->ofp, ".model flat\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 (); + + } + + if (state->syntax & ASM_SYNTAX_MASM) { + masm_flush_data_line (); + } + + emit_pending_extern_symbols (); + + if (vec_dllexports.length) { + + char *p, *tmp; + + const char *name; + long i; + + FILE *fp; + + if ((p = strrchr (state->ofile, '.'))) { + + tmp = xmalloc (p - state->ofile + 5); + sprintf (tmp, "%.*s.def", (int) (p - state->ofile), state->ofile); + + } else { + + tmp = xmalloc (strlen (state->ofile) + 5); + sprintf (tmp, "%s.def", state->ofile); + + } + + if ((fp = fopen (tmp, "w"))) { + + fprintf (fp, "EXPORTS\n"); + + for (i = 0; i < vec_dllexports.length; i++) { + + if ((name = asm_global_symbol_name (vec_dllexports.data[i]))) { + fprintf (fp, " %s\n", name); + } + + } + + fclose (fp); + + } + + } + + if (state->ofp) { + + if (state->syntax & ASM_SYNTAX_MASM) { + + masm_flush_data_line (); + fprintf (state->ofp, "end\n"); + + } + + } + +} diff --git a/lib.c b/lib.c index d833b26..c5dfed4 100755 --- a/lib.c +++ b/lib.c @@ -31,24 +31,29 @@ struct cc_option { #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_MAX_ERRORS 8 -#define CC_OPTION_MLONG64 9 -#define CC_OPTION_OUTFILE 10 -#define CC_OPTION_PEDANTIC 11 -#define CC_OPTION_PREPOCESS 12 -#define CC_OPTION_STD 13 -#define CC_OPTION_TRANDITIONAL_LINEMARKERS 14 -#define CC_OPTION_UNDEF 15 +#define CC_OPTION_BITS32 1 +#define CC_OPTION_BITS64 2 +#define CC_OPTION_COMPILE 3 +#define CC_OPTION_DEFINE 4 +#define CC_OPTION_HELP 5 +#define CC_OPTION_INCLUDE 6 +#define CC_OPTION_NO_LEADING_UNDERSCORE 7 +#define CC_OPTION_NO_LINEMARKERS 8 +#define CC_OPTION_MASM 9 +#define CC_OPTION_MAX_ERRORS 10 +#define CC_OPTION_MLONG64 11 +#define CC_OPTION_OUTFILE 12 +#define CC_OPTION_PEDANTIC 13 +#define CC_OPTION_PREPOCESS 14 +#define CC_OPTION_STD 15 +#define CC_OPTION_TRANDITIONAL_LINEMARKERS 16 +#define CC_OPTION_UNDEF 17 static struct cc_option opts[] = { + { "-m32", CC_OPTION_BITS32, CC_OPTION_NO_ARG }, + { "-m64", CC_OPTION_BITS64, CC_OPTION_NO_ARG }, + { "-pedantic", CC_OPTION_PEDANTIC, CC_OPTION_NO_ARG }, { "-std", CC_OPTION_STD, CC_OPTION_EQUALS_ARG }, @@ -111,6 +116,10 @@ static void print_usage (void) { fprintf (stderr, " -E Preprocess only; do not compile.\n"); fprintf (stderr, " -S Compile only.\n"); + fprintf (stderr, "\n"); + fprintf (stderr, " -m32 Generate code for Intel 386.\n"); + fprintf (stderr, " -m64 Generate code for AMD64.\n"); + fprintf (stderr, "\n"); fprintf (stderr, " -std= Assume that the input sources are for\n"); fprintf (stderr, " .\n"); @@ -266,6 +275,8 @@ void parse_args (int argc, char **argv, int optind) { } + state->bits = 32; + while (optind < argc) { r = argv[optind++]; @@ -342,6 +353,20 @@ void parse_args (int argc, char **argv, int optind) { switch (popt->idx) { + case CC_OPTION_BITS32: { + + state->bits = 32; + break; + + } + + case CC_OPTION_BITS64: { + + state->bits = 64; + break; + + } + case CC_OPTION_COMPILE: { state->mode = CC_MODE_COMPILE; @@ -349,7 +374,7 @@ void parse_args (int argc, char **argv, int optind) { } - case CC_OPTION_DEFINE : { + case CC_OPTION_DEFINE: { char *arg; diff --git a/parse.c b/parse.c index bc06c88..017890e 100644 --- a/parse.c +++ b/parse.c @@ -1,181 +1,110 @@ /****************************************************************************** * @file parse.c *****************************************************************************/ -#include #include #include #include "cc.h" #include "expr.h" +#include "token.h" #include "lib.h" #include "parse.h" +#include "int64.h" #include "report.h" -#include "token.h" - -static enum token_kind parsed_calling_convention = TOK_EOF; - -static struct vector vec_dllexports = { 0 }; -static int parsed_dllexport = 0; -static int parsed_dllimport = 0; - -#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_function_preserve_assignment64_regs = 0; -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) { +#include "vector.h" - rhs_last_pointer_depth = depth; - rhs_last_pointed_size = depth > 1 ? DATA_PTR : (size > 0 ? size : (DATA_INT & 0x1f)); +struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES]; +int typedef_name_count = 0; +int anonymous_aggregate_owner_id = 1; -} - -static void clear_rhs_last_pointer_info (void) { - - rhs_last_pointer_depth = 0; - rhs_last_pointed_size = 0; - -} +enum token_kind last_found_member_calling_convention = TOK_EOF; +enum token_kind postfix_member_calling_convention = TOK_EOF; +enum token_kind parsed_calling_convention = TOK_EOF; -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 int current_function_param_stack_bytes = 0; +struct vector vec_dllexports = { 0 }; +int parsed_dllexport = 0; +int parsed_dllimport = 0; -static enum token_kind current_function_calling_convention = TOK_EOF; +int parsed_type_size = DATA_NONE; +int parsed_type_is_inline = 0; +int parsed_type_is_void = 0; +int parsed_type_is_unsigned = 0; +int parsed_type_is_floating = 0; +int parsed_type_only_qualifiers = 0; -static struct local_symbol *pending_struct_return_lhs = 0; -static const char *pending_struct_return_global_name = 0; +int parsed_type_is_aggregate = 0; +int parsed_type_has_tag = 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; +char parsed_type_tag_name[128]; +char last_cast_type_tag_name[128]; -static int suppress_next_struct_return_scalar_store = 0; -static int token_identifier_is_function_call_rhs_now (void); +int last_cast_type_object_size = 0; +int last_cast_type_is_floating = 0; +int floating_rhs_result_in_eax_bool = 0; -static int assignment32_stop_before_condition_operator = 0; -static int assignment64_stop_before_condition_operator = 0; +int parsed_type_is_array_typedef = 0; +int parsed_type_array_element_size = DATA_NONE; -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; +long parsed_type_array_count = 1; -static int parsed_type_is_aggregate = 0; -static int parsed_type_has_tag = 0; +int declarator_depth = 0; +int declarator_has_function = 0; +int declarator_function_is_pointer = 0; +int declarator_function_param_count = 0; +int declarator_function_has_prototype = 0; +int declarator_function_is_variadic = 0; +int declarator_array_unsized = 0; -static char parsed_type_tag_name[128]; -static char last_cast_type_tag_name[128]; +int declarator_is_pointer = 0; +int declarator_pointer_depth = 0; +int declarator_has_array = 0; -static int last_cast_type_object_size = 0; -static int last_cast_type_is_floating = 0; -static int floating_rhs_result_in_eax_bool = 0; +int declarator_dllexport = 0; +int declarator_dllimport = 0; -static int parsed_type_is_array_typedef = 0; -static int parsed_type_array_element_size = DATA_NONE; +enum token_kind declarator_calling_convention = TOK_EOF; +int global_initializer_accept_symbol_addresses = 0; -static long parsed_type_array_count = 1; +long declarator_array_count = 1; +long declarator_first_array_count = 1; +long declarator_last_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; +int declarator_array_dimensions = 0; +int parsed_storage_class = STORAGE_NONE; -static int declarator_is_pointer = 0; -static int declarator_pointer_depth = 0; -static int declarator_has_array = 0; +int parsed_field_sizes[MAX_AGG_FIELDS]; +int parsed_field_count = 0; -static int declarator_dllexport = 0; -static int declarator_dllimport = 0; +struct member_info_entry member_infos[MAX_MEMBER_INFOS]; +int member_info_count = 0; -static enum token_kind declarator_calling_convention = TOK_EOF; +const char *last_found_member_tag_name = 0; +int last_found_member_is_unsigned = 0; -static int global_initializer_accept_symbol_addresses = 0; -static void masm_flush_data_line (void); +struct pending_param pending_params[MAX_PENDING_PARAMS]; -static long declarator_array_count = 1; -static long declarator_first_array_count = 1; -static long declarator_last_array_count = 1; +int pending_param_count; +int preserve_pending_params; -static int declarator_array_dimensions = 0; +const char *last_declarator_name_start = 0; +const char *last_declarator_name_caret = 0; -#define STORAGE_NONE 0 -#define STORAGE_EXTERN 1 -#define STORAGE_STATIC 2 -#define STORAGE_TYPEDEF 3 +unsigned long last_declarator_name_line = 0; -static int parsed_storage_class = STORAGE_NONE; -#define MAX_GLOBAL_INIT_FIELDS 262144 -#define MAX_AGG_FIELDS 512 +int capture_declarator_name_location = 0; +int captured_declarator_name_location = 0; -static int parsed_field_sizes[MAX_AGG_FIELDS]; -static int parsed_field_count = 0; +const char *captured_declarator_name_start = 0; +const char *captured_declarator_name_caret = 0; -#define MAX_STRING_INIT_BYTES 4096 +unsigned long captured_declarator_name_line = 0; -static void clear_parsed_fields (void) { +void clear_parsed_fields (void) { parsed_field_count = 0; } -static void append_parsed_field (int size) { +void append_parsed_field (int size) { if (parsed_field_count < MAX_AGG_FIELDS) { parsed_field_sizes[parsed_field_count++] = size; @@ -183,446 +112,538 @@ static void append_parsed_field (int size) { } -static int fields_storage_size (const int *sizes, int count) { +int is_string_token (void) { + return tok.kind == TOK_PPSTR || tok.kind == TOK_LSTR; +} - int total = 0, i; - - for (i = 0; i < count; i++) { +int expect (enum token_kind k, const char *what) { + + if (tok.kind == k) { - if (sizes[i] < 0) { - total += -sizes[i]; - } else { - total += sizes[i]; - } + get_token (); + return 1; } - return total; + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected %s", what); + return 0; } -#define MAX_MEMBER_INFOS 4096 - -struct member_info_entry { +int declarator_object_size (int base_size) { - char *name; - int offset; - int size; - int elem_size; - int pointer_depth; + 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; - int is_array; - int is_floating; - int is_unsigned; + } - char *tag_name; - char *owner_tag_name; + if (declarator_is_pointer) { + return DATA_PTR; + } - enum token_kind calling_convention; - int owner_size; - -}; - -static struct member_info_entry member_infos[MAX_MEMBER_INFOS]; -static int member_info_count = 0; + return base_size; -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; +struct enum_const_entry enum_constants[MAX_ENUM_CONSTANTS]; +int enum_constant_count = 0; -static const char *last_found_member_tag_name = 0; -static int last_found_member_is_unsigned = 0; +struct typedef_entry *find_typedef_name (const char *name) { -static enum token_kind last_found_member_calling_convention = TOK_EOF; -static enum token_kind postfix_member_calling_convention = TOK_EOF; + int i; + + if (!name) { + return 0; + } -static int postfix_member_offset = 0; -static int postfix_member_size = 0; + for (i = 0; i < typedef_name_count; i++) { + + if (strcmp (typedef_names[i].name, name) == 0) { + return &typedef_names[i]; + } + + } + + return 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) { +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; } - if (member_info_count >= MAX_MEMBER_INFOS) { + entry = find_typedef_name (name); + + if (!entry) { 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].calling_convention = (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention; - 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; + 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->calling_convention = TOK_EOF; + entry->field_count = 0; - member_info_count++; + 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 int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) { +static int is_current_typedef_name (void) { - int best; - int i; - - last_found_member_tag_name = 0; - last_found_member_is_unsigned = 0; - last_found_member_calling_convention = TOK_EOF; + if (tok.kind != TOK_IDENT || !tok.ident) { + return 0; + } - if (!name) { + if (find_local_symbol (tok.ident)) { return 0; } - best = -1; + return find_typedef_name (tok.ident) != 0; + +} + +static void load_aggregate_tag_fields (struct aggregate_tag_entry *entry) { + + int i; + clear_parsed_fields (); - for (i = member_info_count - 1; i >= 0; i--) { + if (!entry) { + return; + } - if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) { - - best = i; - break; - - } + 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]); } - 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; - last_found_member_calling_convention = member_infos[best].calling_convention; +} - if (offset) { - *offset = member_infos[best].offset; - } +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 (size) { - *size = member_infos[best].size; + if (!name) { + return; } - if (elem_size) { - *elem_size = member_infos[best].elem_size; - } + entry = find_aggregate_tag (name, is_union); - if (pointer_depth) { - *pointer_depth = member_infos[best].pointer_depth; - } + if (!entry) { - if (is_array) { - *is_array = member_infos[best].is_array; - } + if (aggregate_tag_count >= MAX_AGG_TAGS) { + return; + } + + entry = &aggregate_tags[aggregate_tag_count++]; + entry->name = xstrdup (name); + entry->is_union = is_union; - if (is_floating) { - *is_floating = member_infos[best].is_floating; } - return 1; - -} + 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 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) { +static struct enum_const_entry *find_enum_constant (const char *name) { - int best; int i; - last_found_member_tag_name = 0; - last_found_member_is_unsigned = 0; - last_found_member_calling_convention = TOK_EOF; - 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); + for (i = 0; i < enum_constant_count; i++) { + + if (strcmp (enum_constants[i].name, name) == 0) { + return &enum_constants[i]; + } + } - best = -1; + 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 (owner_tag_name && owner_tag_name[0]) { + if (!name || !*name) { + return; + } - 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; - - } - - } + 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 (best < 0) { + if (enum_constant_count >= MAX_ENUM_CONSTANTS) { - 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; - } - - } - - } + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "too many enum constants"); + return; } - if (best < 0) { + entry = &enum_constants[enum_constant_count++]; + entry->name = xstrdup (name); + entry->value = value; + +} + +int token_is_ident (void) { + return tok.kind == TOK_IDENT; +} + +static void parse_enum_body (void) { + + int64_s value; + zext64 (&value, 0); - for (i = member_info_count - 1; i >= 0; i--) { + expect (TOK_LBRACE, "{"); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + + if (!token_is_ident ()) { - if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0 - && member_infos[i].offset + member_infos[i].size <= max_size) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected enum constant"); - if (best < 0 || member_infos[i].offset < member_infos[best].offset) { - best = i; - } + 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; + } } - if (best >= 0) { + expect (TOK_RBRACE, "}"); + +} + +static void parse_decl_modifier (void) { + + if (tok.kind == TOK_STDCALL) { - last_found_member_tag_name = member_infos[best].tag_name; - last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0; - last_found_member_calling_convention = member_infos[best].calling_convention; - - if (offset) { - *offset = member_infos[best].offset; - } - - if (size) { - *size = member_infos[best].size; + if (parsed_calling_convention == TOK_EOF && declarator_calling_convention == TOK_EOF) { + parsed_calling_convention = TOK_STDCALL; } + + } else if (tok.kind == TOK_DLLEXPORT) { + + if (parsed_dllexport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate '__dllexport'"); + } else if (parsed_dllimport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "'__dllexport' and '__dllimport' cannot both be specified"); + } else { - if (elem_size) { - *elem_size = member_infos[best].elem_size; - } + declarator_dllexport = 1; + parsed_dllexport = 1; - if (pointer_depth) { - *pointer_depth = member_infos[best].pointer_depth; } + + } else if (tok.kind == TOK_DLLIMPORT) { + + if (parsed_dllimport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate '__dllimport'"); + } else if (parsed_dllexport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "'__dllexport' and '__dllimport' cannot both be specified"); + } else { - if (is_array) { - *is_array = member_infos[best].is_array; - } + declarator_dllimport = 1; + parsed_dllimport = 1; - 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); + get_token (); } -static const char *find_member_tag_name (const char *name) { +void parse_declarator_inner (char **out_name) { - int i; + while (tok.kind == TOK_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { + + if (tok.kind == TOK_STDCALL) { + + if (declarator_calling_convention == TOK_EOF && parsed_calling_convention == TOK_EOF) { + declarator_calling_convention = tok.kind; + } + + get_token (); + + } else { + parse_decl_modifier (); + } - if (!name) { - return 0; } - for (i = member_info_count - 1; i >= 0; i--) { + while (tok.kind == TOK_STAR) { - if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) { - return member_infos[i].tag_name; + declarator_is_pointer = 1; + declarator_pointer_depth++; + + get_token (); + + while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT || tok.kind == TOK_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { + + if (tok.kind == TOK_STDCALL) { + + if (declarator_calling_convention == TOK_EOF && parsed_calling_convention == TOK_EOF) { + declarator_calling_convention = tok.kind; + } + + get_token (); + + } else if (tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { + parse_decl_modifier (); + } else { + get_token (); + } + } } - return 0; + parse_direct_declarator (out_name); } -#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) { +void clear_pending_params (void) { int i; - if (!name) { - return 0; - } - - for (i = 0; i < aggregate_tag_count; i++) { + for (i = 0; i < pending_param_count; i++) { - if (aggregate_tags[i].is_union == is_union && strcmp (aggregate_tags[i].name, name) == 0) { - return &aggregate_tags[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; + pending_params[i].pointed_is_floating = 0; + pending_params[i].pointed_is_unsigned = 0; + pending_params[i].knr_declared = 0; + + if (pending_params[i].pointed_tag_name) { + + free (pending_params[i].pointed_tag_name); + pending_params[i].pointed_tag_name = 0; + } } - return 0; + pending_param_count = 0; } -static void save_aggregate_tag (const char *name, int is_union, int size, const int *field_sizes, int field_count) { +void parse_declarator (char **out_name) { - struct aggregate_tag_entry *entry; - int i; + int top_level = (declarator_depth == 0); - if (!name) { - return; - } + 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; + + enum token_kind saved_declarator_calling_convention = declarator_calling_convention; - entry = find_aggregate_tag (name, is_union); + int saved_declarator_dllexport = declarator_dllexport; + int saved_declarator_dllimport = declarator_dllimport; - if (!entry) { + unsigned long saved_captured_declarator_name_line = captured_declarator_name_line; - if (aggregate_tag_count >= MAX_AGG_TAGS) { - return; + if (top_level) { + + if (!preserve_pending_params) { + clear_pending_params (); } - entry = &aggregate_tags[aggregate_tag_count++]; - entry->name = xstrdup (name); - entry->is_union = is_union; + declarator_calling_convention = TOK_EOF; + capture_declarator_name_location = 1; + + declarator_dllexport = 0; + declarator_dllimport = 0; + + captured_declarator_name_location = 0; + captured_declarator_name_start = 0; + captured_declarator_name_caret = 0; + captured_declarator_name_line = 0; } - entry->size = size; - entry->field_count = 0; + declarator_depth++; - 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 (); + 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_first_array_count = 1; + declarator_last_array_count = 1; + declarator_array_dimensions = 0; - if (!entry) { - return; - } + parse_declarator_inner (out_name); - parsed_type_size = entry->size; - parsed_type_is_aggregate = 1; - parsed_type_is_void = 0; - parsed_type_has_tag = 1; + 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_first_array_count = declarator_array_count; + declarator_last_array_count = declarator_array_count; + declarator_array_dimensions = 1; + + if (parsed_type_array_element_size > 0) { + parsed_type_size = parsed_type_array_element_size; + } - 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; + declarator_depth--; + + if (top_level) { - int field_count; - int field_sizes[MAX_AGG_FIELDS]; + 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; - int array_element_size; - int is_array; + } else { - char *tag_name; - long array_count; + declarator_calling_convention = saved_declarator_calling_convention; + declarator_dllexport = saved_declarator_dllexport; + declarator_dllimport = saved_declarator_dllimport; - enum token_kind calling_convention; + } -}; +} -static struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES]; -static int typedef_name_count = 0; -static void clear_typedef_names (void) { +int _accept (enum token_kind k) { - int i; - - for (i = 0; i < typedef_name_count; i++) { + if (tok.kind == k) { - 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_names[i].calling_convention = TOK_EOF; + get_token (); + return 1; } - typedef_name_count = 0; + return 0; } -static struct typedef_entry *find_typedef_name (const char *name) { +static const char *find_member_tag_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]; + 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; } } @@ -631,36771 +652,605 @@ static struct typedef_entry *find_typedef_name (const char *name) { } -struct local_symbol; -static struct local_symbol *find_local_symbol (const char *name); - -static int is_current_typedef_name (void) { +static int parse_sizeof_member_expr_size (int leading_stars, int *out_size) { - if (tok.kind != TOK_IDENT || !tok.ident) { - return 0; - } - - if (find_local_symbol (tok.ident)) { - return 0; - } + int size = DATA_INT & 0x1f; + int pointer_depth = 0; + int pointed_size = 0; + int is_array = 0; + int array_element_size = 0; + int final_pointer_depth = 0; + int final_pointed_size = 0; + int final_is_array = 0; + int final_array_element_size = 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, enum token_kind calling_convention, const int *field_sizes, int field_count) { - - struct typedef_entry *entry; - int i; + struct local_symbol *local; - if (!name || !*name) { - return; + if (tok.kind != TOK_IDENT) { + return 0; } - entry = find_typedef_name (name); + local = find_local_symbol (tok.ident); - if (!entry) { + if (local) { - 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; + size = local->size; - } + pointer_depth = local->pointer_depth; + pointed_size = local->pointed_size; - entry = &typedef_names[typedef_name_count++]; - entry->name = xstrdup (name); - entry->tag_name = 0; - entry->calling_convention = TOK_EOF; - - } - - if (entry->tag_name) { + is_array = local->is_array; + array_element_size = local->array_element_size; - free (entry->tag_name); - entry->tag_name = 0; + } 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); + array_element_size = get_global_symbol_array_element_size (tok.ident); - 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); + } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", tok.ident ? tok.ident : ""); } - 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; - entry->calling_convention = calling_convention; + final_pointer_depth = pointer_depth; + final_pointed_size = pointed_size; + final_is_array = is_array; + final_array_element_size = array_element_size; - for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) { - entry->field_sizes[entry->field_count++] = field_sizes[i]; - } + get_token (); - if (is_aggregate && !parsed_type_tag_name[0] && name && *name) { + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { - int mi; + 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)) { - for (mi = 0; mi < member_info_count; mi++) { + 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; + final_array_element_size = member_is_array ? member_size : 0; - if (member_infos[mi].owner_size == size - && member_infos[mi].owner_tag_name == 0) { - member_infos[mi].owner_tag_name = xstrdup (name); + 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 : ""); + } - - } - -} - -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->calling_convention = TOK_EOF; - entry->field_count = 0; - - if (entry->tag_name) { - - free (entry->tag_name); - entry->tag_name = 0; + + get_token (); } - if (name && *name) { - entry->tag_name = xstrdup (name); - } + while (tok.kind == TOK_LBRACK) { - 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; + int depth = 1; + get_token (); - 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; - parsed_calling_convention = TOK_EOF; + while (tok.kind != TOK_EOF && depth > 0) { - append_parsed_field (DATA_INT & 0x1f); - return; + 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_is_array && leading_stars > 0) { + + int remaining_stars = leading_stars - 1; + int elem_size = final_array_element_size > 0 ? final_array_element_size : + (final_pointer_depth > 0 ? DATA_PTR : final_pointed_size); + + /* + * In expressions, an array object decays to a pointer to its first + * element before the leading '*' operators are applied. For an + * array of pointers, sizeof(*array) is therefore pointer-sized, + * not the size of the pointed-to base type. + */ + size = elem_size > 0 ? elem_size : size; + + if (remaining_stars > 0 && final_pointer_depth >= remaining_stars) { + + int remaining_depth = final_pointer_depth - remaining_stars; + size = remaining_depth > 0 ? DATA_PTR : final_pointed_size; + + } + + final_is_array = 0; + final_pointer_depth = 0; + + } else if (final_pointer_depth > 0) { + + size = final_pointer_depth > 1 ? DATA_PTR : final_pointed_size; + final_pointer_depth--; + + } else if (final_is_array && final_array_element_size > 0) { + size = final_array_element_size; + } else if (final_is_array && final_pointed_size > 0) { + size = final_pointed_size; + } + + final_is_array = 0; } - 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_calling_convention = entry->calling_convention; - - parsed_type_has_tag = 0; - parsed_type_tag_name[0] = '\0'; + if (leading_stars > 0 && final_is_array) { - if (entry->tag_name && entry->tag_name[0]) { + int remaining_stars = leading_stars - 1; + int elem_size = final_array_element_size > 0 ? final_array_element_size : + (final_pointer_depth > 0 ? DATA_PTR : final_pointed_size); - 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'; + /* + * In an expression, an array object decays to a pointer to its first + * element before unary '*' is applied. This is needed for plain + * sizeof(*array) as well as for forms that later include subscripts. + * For example, with char *builtins[], sizeof(*builtins) is the size + * of one array element, i.e. pointer-sized, not sizeof(char). + */ + size = elem_size > 0 ? elem_size : size; - parsed_type_has_tag = 1; + if (remaining_stars > 0 && final_pointer_depth >= remaining_stars) { + + int remaining_depth = final_pointer_depth - remaining_stars; + size = remaining_depth > 0 ? DATA_PTR : final_pointed_size; + + } + + final_is_array = 0; + final_pointer_depth = 0; + + } 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; } - for (i = 0; i < entry->field_count; i++) { - append_parsed_field (entry->field_sizes[i]); + if (size < 1) { + size = DATA_INT & 0x1f; } + + *out_size = size; + return 1; } -#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) { +static int parse_octal_escape_value (const char **ps) { - int i; - - if (!name) { - return 0; - } + const char *s = *ps; + int value = 0, i; - for (i = 0; i < enum_constant_count; i++) { + for (i = 0; i < 3; i++) { - if (strcmp (enum_constants[i].name, name) == 0) { - return &enum_constants[i]; + if (*s < '0' || *s > '7') { + break; } + + value = value * 8 + (*s - '0'); + s++; } - return 0; + *ps = s; + return value; } -static void save_enum_constant (const char *name, int64_s value, const char *start, const char *caret) { +static int parse_hex_escape_value (const char **ps) { - struct enum_const_entry *entry; - - if (!name || !*name) { - return; - } + const char *s = *ps; - entry = find_enum_constant (name); + int value = 0; + int any = 0; - if (entry) { + while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "duplicate enum constant '%s'", name); - return; + 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 (enum_constant_count >= MAX_ENUM_CONSTANTS) { - - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "too many enum constants"); - return; - + if (!any) { + value = 'x'; } - entry = &enum_constants[enum_constant_count++]; - entry->name = xstrdup (name); - entry->value = value; + *ps = s; + return value; } -int resolve_enum_constant (const char *name, int64_s *out) { +void append_global_init_value (int64_s *values, int max_values, int *count, unsigned int value, int elem_size) { - struct enum_const_entry *entry = find_enum_constant (name); - - if (!entry) { - return 0; - } + unsigned int mask = 0xffU; - if (out) { - *out = entry->value; + if (elem_size == (DATA_SHORT & 0x1f)) { + mask = 0xffffU; } - 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; - + if (*count < max_values) { + zext64 (&values[*count], value & mask); } - enum_constant_count = 0; + (*count)++; } -#define MAX_GLOBAL_SYMBOLS 4096 - -#define GLOBAL_SYMBOL_OBJECT 1 -#define GLOBAL_SYMBOL_FUNCTION 2 +void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3) { -struct global_symbol_entry { - - char *name; - - int is_unsigned, is_extern; - int is_dllimport; - int kind, size; - - int array_element_size; - int is_array; - int array_dimensions; - - long array_count; - - int is_floating; - int pointer_depth; - int pointed_size; - int pointed_is_floating; - int pointed_is_unsigned; - int returns_void; - - int param_count; - int has_prototype; - int is_variadic; - - int param_sizes[128]; - int param_unsigneds[128]; - int param_floatings[128]; - - int import_call_stack_bytes; - int is_implicit; - int extern_emitted; - - enum token_kind calling_convention; - 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; + int paren = 0, brace = 0, brack = 0; - for (i = 0; i < global_symbol_count; i++) { + while (tok.kind != TOK_EOF) { - free (global_symbols[i].name); - - global_symbols[i].name = 0; - global_symbols[i].kind = 0; - global_symbols[i].size = 0; + if (paren == 0 && brace == 0 && brack == 0) { - global_symbols[i].array_element_size = 0; - global_symbols[i].is_array = 0; - global_symbols[i].array_dimensions = 0; + if (tok.kind == stop1 || tok.kind == stop2 || tok.kind == stop3) { + return; + } - global_symbols[i].is_dllimport = 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].calling_convention = TOK_EOF; + if (tok.kind == TOK_LPAREN) { + paren++; + } else if (tok.kind == TOK_RPAREN) { - { - - int pi; - - for (pi = 0; pi < 128; pi++) { + if (paren > 0) { + paren--; + } else { - global_symbols[i].param_sizes[pi] = 0; - global_symbols[i].param_unsigneds[pi] = 0; - global_symbols[i].param_floatings[pi] = 0; + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } } - } + } else if (tok.kind == TOK_LBRACE) { + brace++; + } else if (tok.kind == TOK_RBRACE) { - global_symbols[i].import_call_stack_bytes = 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 (brace > 0) { + brace--; + } else { + + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } + + } - if (global_symbols[i].tag_name) { + } else if (tok.kind == TOK_LBRACK) { + brack++; + } else if (tok.kind == TOK_RBRACK) { - free (global_symbols[i].tag_name); - global_symbols[i].tag_name = 0; + if (brack > 0) { + brack--; + } else { + + if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) { + return; + } + + } } + + get_token (); } - - global_symbol_count = 0; } -static int find_global_symbol (const char *name) { +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; + last_found_member_calling_convention = TOK_EOF; + if (!name) { - return -1; + return 0; } - for (i = 0; i < global_symbol_count; i++) { + 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); + } - if (strcmp (global_symbols[i].name, name) == 0) { - return i; - } + best = -1; - } + if (owner_tag_name && owner_tag_name[0]) { - return -1; - -} - -static int asm_symbol_is_internal (const char *name) { - - if (!name || !*name) { - return 1; - } + 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 (name[0] == '.') { - return 1; } - if (name[0] == 'L') { + if (best < 0) { - if (name[1] >= '0' && name[1] <= '9') { - return 1; - } + 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 (name[1] == 'C' && name[2] >= '0' && name[2] <= '9') { - return 1; } } - return 0; - -} - -static int global_symbol_stdcall_stack_bytes (const char *name) { - - int i = find_global_symbol (name); - int pi; - int bytes = 0; - - if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || global_symbols[i].calling_convention != TOK_STDCALL || + if (best < 0) { - global_symbols[i].is_variadic) { - return 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; + } + + } + + } } - for (pi = 0; pi < global_symbols[i].param_count && pi < 128; pi++) { + if (best >= 0) { - int size = global_symbols[i].param_sizes[pi]; + last_found_member_tag_name = member_infos[best].tag_name; + last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0; + last_found_member_calling_convention = member_infos[best].calling_convention; - if (size < 1) { - size = DATA_PTR & 0x1f; + if (offset) { + *offset = member_infos[best].offset; } - bytes += ((size + (DATA_PTR & 0x1f) - 1) / (DATA_PTR & 0x1f)) * (DATA_PTR & 0x1f); - - } + 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; - if (bytes <= 0 && global_symbols[i].import_call_stack_bytes > 0) { - bytes = global_symbols[i].import_call_stack_bytes; } - return bytes; + return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating); } -static void remember_global_symbol_import_call_stack_bytes (const char *name, int bytes) { +int parse_constexpr_null_member_address_after_lparen (int64_s *out) { - int i = find_global_symbol (name); + char current_tag_name[128]; - if (i < 0 || bytes <= 0) { - return; - } + int current_object_size; + unsigned long total_offset = 0; - if (global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION || !global_symbols[i].is_dllimport || global_symbols[i].calling_convention != TOK_STDCALL) { - return; + if (tok.kind != TOK_LPAREN) { + return 0; } - if (global_symbol_stdcall_stack_bytes (name) <= 0) { - global_symbols[i].import_call_stack_bytes = bytes; - } - -} - -static const char *asm_global_symbol_name (const char *name) { - - static char buffers[8][512]; - static int index = 0; + get_token (); - char *out, suffix[32]; - int stdcall_bytes, symbol_index; + if (tok.kind != TOK_LPAREN) { + return 0; + } - unsigned long avail, len; + get_token (); - if (asm_symbol_is_internal (name)) { - return name; + if (!parse_cast_type_name (0, 0, 0)) { + return 0; } - index = (index + 1) & 7; + current_tag_name[0] = '\0'; + + if (last_cast_type_tag_name[0]) { - out = buffers[index]; - len = strlen (name); + strncpy (current_tag_name, last_cast_type_tag_name, sizeof (current_tag_name) - 1); + current_tag_name[sizeof (current_tag_name) - 1] = '\0'; - stdcall_bytes = global_symbol_stdcall_stack_bytes (name); - suffix[0] = '\0'; + } - symbol_index = find_global_symbol (name); + current_object_size = last_cast_type_object_size; - if (symbol_index >= 0 && global_symbols[symbol_index].kind == GLOBAL_SYMBOL_FUNCTION && global_symbols[symbol_index].calling_convention == TOK_STDCALL && !global_symbols[symbol_index].is_variadic) { - sprintf (suffix, "@%d", stdcall_bytes); + 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; } - avail = sizeof (buffers[0]) - 1 - strlen (suffix); + get_token (); + expect (TOK_RPAREN, ")"); - if (!state->no_leading_underscore) { + while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { - if (avail > 0) { - avail--; + 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; + } - if (len > avail) { - len = avail; + 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; + } - out[0] = '_'; + 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)); - memcpy (out + 1, name, len); - out[len + 1] = 0; - - } else { - - if (len > avail) { - len = avail; } - memcpy (out, name, len); - out[len] = 0; + 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; + } } - if (suffix[0]) { - strcat (out, suffix); + expect (TOK_RPAREN, ")"); + + if (out) { + zext64 (out, total_offset); } - return out; + return 1; } -static const char *asm_global_import_symbol_name (const char *name) { +int parsed_string_initializer_elem_size = DATA_CHAR & 0x1f; - static char buffers[8][512]; - static int index = 0; - - const char *decorated; - char *out; - - unsigned long len; - - if (!(decorated = asm_global_symbol_name (name)) || asm_symbol_is_internal (name)) { - return decorated; - } - - index = (index + 1) & 7; - - out = buffers[index]; - len = strlen (decorated); - - if (len > sizeof (buffers[0]) - 7) { - len = sizeof (buffers[0]) - 7; - } - - sprintf (out, "__imp_"); - - memcpy (out + 6, decorated, len); - out[len + 6] = 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_dllimport (const char *name, int is_dllimport) { - - int i = find_global_symbol (name); - - if (i >= 0) { - - global_symbols[i].is_dllimport = is_dllimport ? 1 : 0; - - if (is_dllimport) { - global_symbols[i].is_extern = 1; - } - - } - -} - -static int get_global_symbol_dllimport (const char *name) { - - int i = find_global_symbol (name); - - if (i >= 0) { - return global_symbols[i].is_dllimport ? 1 : 0; - } - - 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_dimensions (const char *name, int dims) { - - int i = find_global_symbol (name); - - if (i >= 0) { - global_symbols[i].array_dimensions = dims > 0 ? dims : 0; - } - -} - -static int get_global_symbol_array_dimensions (const char *name) { - - int i = find_global_symbol (name); - - if (i >= 0) { - return global_symbols[i].array_dimensions; - } - - 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 int get_global_function_returns_floating (const char *name) { - - int i = find_global_symbol (name); - - if (i < 0 || global_symbols[i].kind != GLOBAL_SYMBOL_FUNCTION) { - return 0; - } - - if (global_symbols[i].is_floating) { - return 1; - } - - /* - * Do not infer floating return types from the masked object size. - * - * DATA_INT and DATA_FLOAT both have a 4-byte payload size, so checking - * only (size & 0x1f) makes every int-returning function look like it - * returns float. That breaks expressions such as: - * - * printf("%p", main); - * - * because the function designator gets loaded as a floating object from - * memory instead of as a plain code address. The declaration/definition - * paths already record real floating function returns in is_floating. - */ - return global_symbols[i].is_floating ? 1 : 0; - -} - -static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) { - - int i = find_global_symbol (name); - - if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { - pointed_size = parsed_type_size; - } - - if (i >= 0) { - - global_symbols[i].pointer_depth = pointer_depth; - global_symbols[i].pointed_size = pointed_size; - - global_symbols[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; - global_symbols[i].pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; - - } - -} - -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 int get_global_symbol_pointed_is_floating (const char *name) { - - int i = find_global_symbol (name); - - if (i >= 0) { - return global_symbols[i].pointed_is_floating; - } - - return 0; - -} - -static int get_global_symbol_pointed_is_unsigned (const char *name) { - - int i = find_global_symbol (name); - - if (i >= 0) { - return global_symbols[i].pointed_is_unsigned; - } - - return 0; - -} - -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 void set_global_symbol_calling_convention (const char *name, enum token_kind calling_convention) { - - int i = find_global_symbol (name); - - if (i >= 0) { - global_symbols[i].calling_convention = calling_convention; - } - -} - -static enum token_kind get_global_symbol_calling_convention (const char *name) { - - int i = find_global_symbol (name); - - if (i >= 0) { - return global_symbols[i].calling_convention; - } - - return TOK_EOF; - -} - -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 int get_global_symbol_param_size (const char *name, int param_index) { - - int i = find_global_symbol (name); - - if (i >= 0 && param_index >= 0 && param_index < 128 && global_symbols[i].param_sizes[param_index] > 0) { - return global_symbols[i].param_sizes[param_index]; - } - - return DATA_PTR & 0x1f; - -} - -static int get_global_symbol_param_unsigned (const char *name, int param_index) { - - int i = find_global_symbol (name); - - if (i >= 0 && param_index >= 0 && param_index < 128) { - return global_symbols[i].param_unsigneds[param_index] ? 1 : 0; - } - - return 0; - -} - -static int get_global_symbol_param_floating (const char *name, int param_index) { - - int i = find_global_symbol (name); - - if (i >= 0 && param_index >= 0 && param_index < 128) { - return global_symbols[i].param_floatings[param_index] ? 1 : 0; - } - - 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_dllimport = 0; - 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].pointer_depth = 0; - global_symbols[global_symbol_count].pointed_size = 0; - global_symbols[global_symbol_count].pointed_is_floating = 0; - global_symbols[global_symbol_count].pointed_is_unsigned = 0; - global_symbols[global_symbol_count].returns_void = 0; - global_symbols[global_symbol_count].calling_convention = TOK_EOF; - - { - - int pi; - - for (pi = 0; pi < 128; pi++) { - - global_symbols[global_symbol_count].param_sizes[pi] = 0; - global_symbols[global_symbol_count].param_unsigneds[pi] = 0; - global_symbols[global_symbol_count].param_floatings[pi] = 0; - - } - - } - - global_symbols[global_symbol_count].import_call_stack_bytes = 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].calling_convention = TOK_EOF; - 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); - -static long function_frame_saved_assignment64_bytes (void) { - return current_function_preserve_assignment64_regs ? 12 : 0; -} - -static void emit_function_frame_adjust_text (char **dst, long frame_size) { - - char buf[128]; - long save_bytes; - - int n; - - if (!dst) { - return; - } - - frame_size = (frame_size + 3) & ~3L; - save_bytes = function_frame_saved_assignment64_bytes (); - - if (frame_size <= 0 && save_bytes <= 0) { - return; - } - - if (frame_size > 2147483647L) { - frame_size = 2147483647L; - } - - if (state->syntax & ASM_SYNTAX_INTEL) { - - if (frame_size > 0) { - - n = sprintf (buf, " sub esp, %ld\n", frame_size); - append_inline_text (dst, buf, (size_t) n); - - } - - if (save_bytes) { - - append_inline_text (dst, " push ebx\n", strlen (" push ebx\n")); - append_inline_text (dst, " push esi\n", strlen (" push esi\n")); - append_inline_text (dst, " push edi\n", strlen (" push edi\n")); - - } - - } else { - - if (frame_size > 0) { - - n = sprintf (buf, " subl $%ld, %%esp\n", frame_size); - append_inline_text (dst, buf, (size_t) n); - - } - - if (save_bytes) { - - append_inline_text (dst, " pushl %ebx\n", strlen (" pushl %ebx\n")); - append_inline_text (dst, " pushl %esi\n", strlen (" pushl %esi\n")); - append_inline_text (dst, " pushl %edi\n", strlen (" pushl %edi\n")); - - } - - } - -} - -static void emit_function_frame_restore_text (char **dst, long frame_size) { - - if (!dst || !function_frame_saved_assignment64_bytes ()) { - return; - } - - (void) frame_size; - - if (state->syntax & ASM_SYNTAX_INTEL) { - - append_inline_text (dst, " pop edi\n", strlen (" pop edi\n")); - append_inline_text (dst, " pop esi\n", strlen (" pop esi\n")); - append_inline_text (dst, " pop ebx\n", strlen (" pop ebx\n")); - - } else { - - append_inline_text (dst, " popl %edi\n", strlen (" popl %edi\n")); - append_inline_text (dst, " popl %esi\n", strlen (" popl %esi\n")); - append_inline_text (dst, " popl %ebx\n", strlen (" popl %ebx\n")); - - } - -} - -static char *replace_function_frame_placeholder (const char *text, long frame_size) { - - const char *frame_marker = "__SCC_FRAME_PLACEHOLDER__\n"; - const char *restore_marker = "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"; - - const char *pr, *pf, *p; - const char *marker, *last; - - char *out = 0; - size_t marker_len; - - if (!text) { - return 0; - } - - last = text; - - for (;;) { - - pf = strstr (last, frame_marker); - pr = strstr (last, restore_marker); - - if (!pf && !pr) { - break; - } - - if (pf && (!pr || pf < pr)) { - p = pf; - marker = frame_marker; - } else { - p = pr; - marker = restore_marker; - } - - marker_len = strlen (marker); - append_inline_text (&out, last, (size_t) (p - last)); - - if (marker == frame_marker) { - emit_function_frame_adjust_text (&out, frame_size); - } else { - emit_function_frame_restore_text (&out, frame_size); - } - - last = p + marker_len; - - } - - append_inline_text (&out, last, strlen (last)); - return out; - -} - -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 + 1UL) & 0xffffffffUL)); - } - - 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); - - } - - } - - scc_close (*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 array_dimensions; - - int pointer_depth; - int pointed_size; - int pointed_is_floating; - int pointed_is_unsigned; - - 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 aggregate_tag_size_or_zero (const char *tag_name) { - - struct aggregate_tag_entry *entry; - - if (!tag_name || !tag_name[0]) { - return 0; - } - - entry = find_aggregate_tag (tag_name, 0); - - if (entry && entry->size > 0) { - return entry->size; - } - - return 0; - -} - -static int local_symbol_count = 0; - -static long current_local_stack_size = 0; -static long current_block_cleanup_bytes = 0; - -static long current_function_frame_size = 0; - -static int current_function_frame_label = -1; -static int current_function_frame_deferred = 0; -static int current_function_frame_enabled = 0; -static int current_function_uses_single_frame = 0; - -static int next_function_frame_label = 1; - -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; - current_block_cleanup_bytes = 0; - current_function_frame_size = 0; - current_function_uses_single_frame = 0; - current_function_preserve_assignment64_regs = 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].array_dimensions = 0; - local_symbols[local_symbol_count].pointer_depth = 0; - local_symbols[local_symbol_count].pointed_size = 0; - local_symbols[local_symbol_count].pointed_is_floating = 0; - local_symbols[local_symbol_count].pointed_is_unsigned = 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; - - if (current_local_stack_size > current_function_frame_size) { - current_function_frame_size = current_local_stack_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_symbols[local_symbol_count].array_dimensions = 0; - local_symbols[local_symbol_count].pointer_depth = 0; - local_symbols[local_symbol_count].pointed_size = 0; - local_symbols[local_symbol_count].pointed_is_floating = 0; - local_symbols[local_symbol_count].pointed_is_unsigned = 0; - local_symbols[local_symbol_count].pointed_tag_name = 0; - local_symbols[local_symbol_count].tag_name = 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_array_dimensions (const char *name, int dims) { - - struct local_symbol *sym = find_local_symbol (name); - - if (sym) { - sym->array_dimensions = dims > 0 ? dims : 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; - int pointed_is_floating; - int pointed_is_unsigned; - - char *pointed_tag_name; - int knr_declared; - -}; - -static struct pending_param pending_params[MAX_PENDING_PARAMS]; - -static int pending_param_count = 0; -static int declarator_depth = 0; -static int preserve_pending_params = 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; - pending_params[i].pointed_is_floating = 0; - pending_params[i].pointed_is_unsigned = 0; - pending_params[i].knr_declared = 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, int pointed_is_unsigned) { - - if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { - pointed_size = parsed_type_size; - } - - 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 = (name && *name) ? xstrdup (name) : 0; - 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].knr_declared = 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_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; - pending_params[pending_param_count].pointed_is_unsigned = pointer_depth > 0 ? (pointed_is_unsigned ? 1 : 0) : 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 int find_pending_param_from (const char *name, int start) { - - int i; - - if (!name) { - return -1; - } - - if (start < 0) { - start = 0; - } - - for (i = start; i < pending_param_count; i++) { - - if (pending_params[i].name && strcmp (pending_params[i].name, name) == 0) { - return i; - } - - } - - return -1; - -} - -static int find_pending_param (const char *name) { - return find_pending_param_from (name, 0); -} - -static void update_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size, int pointed_is_unsigned) { - - int i = find_pending_param (name); - - if (i < 0) { - return; - } - - if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { - pointed_size = parsed_type_size; - } - - if (size < 1) { - size = DATA_INT & 0x1f; - } - - if (align < 1) { - align = type_alignment (size); - } - - pending_params[i].size = size; - pending_params[i].align = align; - pending_params[i].is_unsigned = is_unsigned ? 1 : 0; - pending_params[i].is_floating = is_floating ? 1 : 0; - pending_params[i].pointer_depth = pointer_depth; - pending_params[i].pointed_size = pointed_size > 0 ? pointed_size : 0; - pending_params[i].pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; - pending_params[i].pointed_is_unsigned = pointer_depth > 0 ? (pointed_is_unsigned ? 1 : 0) : 0; - - if (pending_params[i].pointed_tag_name) { - - free (pending_params[i].pointed_tag_name); - pending_params[i].pointed_tag_name = 0; - - } - - if (pointer_depth > 0 && parsed_type_tag_name[0]) { - pending_params[i].pointed_tag_name = xstrdup (parsed_type_tag_name); - } - -} - -static int mark_pending_param_knr_declared (const char *name, const char *name_start, const char *name_caret, unsigned long name_line) { - - int i = find_pending_param (name); - - if (i < 0) { - - report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "K&R parameter '%s' is not in function parameter list", name); - return 0; - - } - - if (pending_params[i].knr_declared) { - - report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "duplicate K&R parameter declaration for '%s'", name); - return 0; - - } - - pending_params[i].knr_declared = 1; - return 1; - -} - -static void remove_duplicate_pending_params (void) { - - int i; - - for (i = 0; i < pending_param_count; i++) { - - int j; - - if (!pending_params[i].name) { - continue; - } - - j = i + 1; - - while (j < pending_param_count) { - - if (pending_params[j].name && strcmp (pending_params[i].name, pending_params[j].name) == 0) { - - int k; - - if (pending_params[j].name) { - free (pending_params[j].name); - } - - if (pending_params[j].pointed_tag_name) { - free (pending_params[j].pointed_tag_name); - } - - for (k = j; k + 1 < pending_param_count; k++) { - pending_params[k] = pending_params[k + 1]; - } - - pending_param_count--; - - pending_params[pending_param_count].name = 0; - pending_params[pending_param_count].size = 0; - pending_params[pending_param_count].align = 0; - pending_params[pending_param_count].is_unsigned = 0; - pending_params[pending_param_count].is_floating = 0; - pending_params[pending_param_count].pointer_depth = 0; - pending_params[pending_param_count].pointed_size = 0; - pending_params[pending_param_count].pointed_tag_name = 0; - - continue; - - } - - j++; - - } - - } - -} - -static void copy_pending_params_to_global_symbol (const char *name) { - - int i = find_global_symbol (name); - int pi; - - if (i < 0) { - return; - } - - for (pi = 0; pi < 128; pi++) { - - global_symbols[i].param_sizes[pi] = 0; - global_symbols[i].param_unsigneds[pi] = 0; - global_symbols[i].param_floatings[pi] = 0; - - } - - for (pi = 0; pi < pending_param_count && pi < 128; pi++) { - - global_symbols[i].param_sizes[pi] = pending_params[pi].size; - global_symbols[i].param_unsigneds[pi] = pending_params[pi].is_unsigned; - global_symbols[i].param_floatings[pi] = pending_params[pi].is_floating; - - } - -} - -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 (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { - pointed_size = parsed_type_size; - } - - if (sym) { - - sym->pointer_depth = pointer_depth; - sym->pointed_size = pointed_size; - - sym->pointed_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; - sym->pointed_is_unsigned = pointer_depth > 0 ? (parsed_type_is_unsigned ? 1 : 0) : 0; - - if (sym->pointed_tag_name) { - - free (sym->pointed_tag_name); - sym->pointed_tag_name = 0; - - } - - if ((pointer_depth > 0 || sym->is_array) && 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_is_floating = pending_params[i].pointed_is_floating; - local_symbols[local_symbol_count].pointed_is_unsigned = pending_params[i].pointed_is_unsigned; - local_symbols[local_symbol_count].is_array = 0; - local_symbols[local_symbol_count].array_element_size = 0; - local_symbols[local_symbol_count].array_dimensions = 0; - 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 (state->syntax & ASM_SYNTAX_MASM) { - masm_flush_data_line (); - } - - 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) { - - switch (k) { - - case TOK_IDENT: - - if (token_is_ms_int_type_name ()) { - return 1; - } - - if (is_current_typedef_name ()) { - return 1; - } - - return 0; - - case TOK_AUTO: case TOK_REGISTER: case TOK_STATIC: - case TOK_DLLEXPORT: case TOK_DLLIMPORT: - 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; - - } - - 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; - int saved_declarator_array_dimensions = declarator_array_dimensions; - - long saved_declarator_array_count = declarator_array_count; - long saved_declarator_first_array_count = declarator_first_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; - last_cast_type_is_floating = (is_pointer ? 0 : parsed_type_is_floating); - - 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; - declarator_first_array_count = saved_declarator_first_array_count; - declarator_array_dimensions = saved_declarator_array_dimensions; - - 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 last_deref_cast_type_is_floating = 0; - -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_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; - int saved_declarator_array_dimensions = declarator_array_dimensions; - - long saved_declarator_array_count = declarator_array_count; - long saved_declarator_first_array_count = declarator_first_array_count; - - char *name = 0; - - int size = DATA_INT & 0x1f; - int ok = 0, i; - int cast_is_floating = 0; - - last_deref_cast_type_is_floating = 0; - - 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; - cast_is_floating = parsed_type_is_floating; - - if (tok.kind != TOK_RPAREN) { - - parse_declarator (&name); - - /* - * This parser is called after a leading unary '*', for forms such - * as *(long long *)p. The '*' outside the cast performs the - * dereference, so the size needed by the caller is the pointed-at - * object size, not DATA_PTR. However, forms produced by va_arg() - * for pointer types are one level deeper, e.g. char * becomes - * *(char **). In that case the dereferenced object is itself a - * pointer and must be loaded with pointer width. - */ - if (declarator_is_pointer && declarator_pointer_depth > 1) { - size = DATA_PTR; - } - - } - - if (tok.kind == TOK_RPAREN) { - - get_token (); - ok = 1; - - last_deref_cast_type_is_floating = cast_is_floating; - - } 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_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; - declarator_first_array_count = saved_declarator_first_array_count; - declarator_array_dimensions = saved_declarator_array_dimensions; - - 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 emit_load_floating_rhs_expression_now (int result_size); -static void emit_store_floating_to_local_now (long offset, int size); - -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_inner (char **out_name); -static void parse_declarator (char **out_name); - -static void parse_decl_modifier (void) { - - if (tok.kind == TOK_STDCALL) { - - if (parsed_calling_convention == TOK_EOF && declarator_calling_convention == TOK_EOF) { - parsed_calling_convention = TOK_STDCALL; - } - - } else if (tok.kind == TOK_DLLEXPORT) { - - if (parsed_dllexport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate '__dllexport'"); - } else if (parsed_dllimport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "'__dllexport' and '__dllimport' cannot both be specified"); - } else { - - declarator_dllexport = 1; - parsed_dllexport = 1; - - } - - } else if (tok.kind == TOK_DLLIMPORT) { - - if (parsed_dllimport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate '__dllimport'"); - } else if (parsed_dllexport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "'__dllexport' and '__dllimport' cannot both be specified"); - } else { - - declarator_dllimport = 1; - parsed_dllimport = 1; - - } - - } - - get_token (); - -} - -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; - - parsed_calling_convention = TOK_EOF; - - if (declarator_depth == 0) { - declarator_calling_convention = TOK_EOF; - } - - parsed_dllexport = 0; - parsed_dllimport = 0; - - 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 || - tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT || tok.kind == TOK_STDCALL || 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_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { - - parse_decl_modifier (); - continue; - - } else 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_LONG) { - - parsed_type_size = DATA_DOUBLE; - saw_double = 1; - - } else 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)) { - - int was_array = declarator_has_array; - long count = 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 (!was_array) { - declarator_first_array_count = count; - } - - declarator_has_array = 1; - declarator_array_dimensions++; - - 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)) { - - int old_style_param_start = pending_param_count; - 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)) { - - enum token_kind saved_calling_convention = declarator_calling_convention; - enum token_kind saved_parsed_calling_convention = parsed_calling_convention; - - 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), parsed_type_is_unsigned); - - } - - } else { - saw_void_param_list = 1; - } - - if (param_name) { - free (param_name); - } - - parsed_calling_convention = saved_parsed_calling_convention; - 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_calling_convention = saved_calling_convention; - 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) { - - if (find_pending_param_from (param_name, old_style_param_start) >= 0) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", param_name); - } else { - add_pending_param (param_name, param_size, type_alignment (param_size), 0, 0, unknown_typedef_pointer ? 1 : 0, DATA_INT & 0x1f, 0); - } - - } - - free (param_name); - - declarator_function_param_count++; - declarator_function_has_prototype = 1; - consumed_as_prototype_param = 1; - - } - - if (!consumed_as_prototype_param) { - - if (declarator_depth == 1) { - - if (find_pending_param_from (maybe_type_name, old_style_param_start) >= 0) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", maybe_type_name); - } else { - add_pending_param (maybe_type_name, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0, 0); - } - - } - - 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_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { - - if (tok.kind == TOK_STDCALL) { - - if (declarator_calling_convention == TOK_EOF && parsed_calling_convention == TOK_EOF) { - declarator_calling_convention = tok.kind; - } - - get_token (); - - } else { - parse_decl_modifier (); - } - - } - - 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 || tok.kind == TOK_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { - - if (tok.kind == TOK_STDCALL) { - - if (declarator_calling_convention == TOK_EOF && parsed_calling_convention == TOK_EOF) { - declarator_calling_convention = tok.kind; - } - - get_token (); - - } else if (tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { - parse_decl_modifier (); - } else { - 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; - - enum token_kind saved_declarator_calling_convention = declarator_calling_convention; - - int saved_declarator_dllexport = declarator_dllexport; - int saved_declarator_dllimport = declarator_dllimport; - - unsigned long saved_captured_declarator_name_line = captured_declarator_name_line; - - if (top_level) { - - if (!preserve_pending_params) { - clear_pending_params (); - } - - declarator_calling_convention = TOK_EOF; - capture_declarator_name_location = 1; - - declarator_dllexport = 0; - declarator_dllimport = 0; - - 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_first_array_count = 1; - declarator_last_array_count = 1; - declarator_array_dimensions = 0; - - 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_first_array_count = declarator_array_count; - declarator_last_array_count = declarator_array_count; - declarator_array_dimensions = 1; - - 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; - - } else { - - declarator_calling_convention = saved_declarator_calling_convention; - declarator_dllexport = saved_declarator_dllexport; - declarator_dllimport = saved_declarator_dllimport; - - } - -} - -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_first_array_count = declarator_array_count; - declarator_last_array_count = declarator_array_count; - declarator_array_dimensions = 1; - - 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)) { - - int param_base_size; - - parse_type_spec (); - param_base_size = parsed_type_size; - - for (;;) { - - char *name = 0; - - int param_size; - int param_pointer_depth; - - preserve_pending_params++; - parse_declarator (&name); - preserve_pending_params--; - - if (name) { - - param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size); - param_pointer_depth = declarator_pointer_depth; - - if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { - param_pointer_depth = 1; - } - - if (mark_pending_param_knr_declared (name, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line)) { - - update_pending_param (name, param_size, type_alignment (param_size), - (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), - param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); - - } - - 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_deferred_function_frame_placeholder (void); -static void patch_deferred_function_frame (void); - -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) { - - masm_flush_data_line (); - - 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"); - - emit_deferred_function_frame_placeholder (); - - } else { - - fprintf (state->ofp, "%s:\n", asm_name); - fprintf (state->ofp, " pushl %%ebp\n"); - fprintf (state->ofp, " movl %%esp, %%ebp\n"); - - emit_deferred_function_frame_placeholder (); - - } - -} - -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 emit_deferred_function_frame_placeholder (void) { - - current_function_frame_label = -1; - current_function_frame_deferred = 0; - - if (!state->ofp || !current_function_frame_enabled) { - return; - } - - current_function_frame_label = next_function_frame_label++; - current_function_frame_deferred = 1; - current_function_uses_single_frame = 1; - - fprintf (state->ofp, "__SCC_FRAME_PLACEHOLDER__\n"); - -} - -static void patch_deferred_function_frame (void) { - - current_function_frame_deferred = 0; - current_function_frame_label = -1; - current_function_frame_enabled = 0; - -} - -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_store_floating_to_local_now (long offset, int size) { - - char memref[64]; - - if (!state->ofp) { - return; - } - - if (state->syntax & ASM_SYNTAX_INTEL) { - - format_intel_ebp_offset (memref, sizeof (memref), 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, " fstp%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", 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) { - - patch_deferred_function_frame (); - - 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"); - } - - } - - if (current_function_preserve_assignment64_regs) { - fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"); - } - - fprintf (state->ofp, " leave\n"); - - if (current_function_calling_convention == TOK_STDCALL && current_function_param_stack_bytes > 0) { - fprintf (state->ofp, " ret %d\n", current_function_param_stack_bytes); - } else { - 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"); - } - - } - - if (current_function_preserve_assignment64_regs) { - fprintf (state->ofp, "__SCC_RESTORE_ASSIGNMENT64_REGS__\n"); - } - - fprintf (state->ofp, " leave\n"); - - if (current_function_calling_convention == TOK_STDCALL && current_function_param_stack_bytes > 0) { - - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " ret %d\n", current_function_param_stack_bytes); - } else { - fprintf (state->ofp, " ret $%d\n", current_function_param_stack_bytes); - } - - } else { - fprintf (state->ofp, " ret\n"); - } - - } - -} - -static void emit_preserve_assignment64_regs (enum token_kind op) { - - if (current_function_frame_deferred) { - - current_function_preserve_assignment64_regs = 1; - return; - - } - - 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 (current_function_frame_deferred) { - return; - } - - 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_array_element_size_now (int base_size) { - - int object_size; - - if (!declarator_has_array) { - return 0; - } - - if (declarator_is_pointer) { - return DATA_PTR; - } - - /* - * For an array object, the element reached by one subscript is the - * complete row after the first dimension, not always the scalar base - * type. For example, with: - * - * static const char wday_name[7][3]; - * - * wday_name[i] has type char [3] and decays to the address of that - * three-byte row when passed to %.3s. Recording element size as char - * made expression code emit a byte load from wday_name + i instead of - * the row address wday_name + i * 3. - */ - if (declarator_array_dimensions > 1 && declarator_first_array_count > 0) { - - object_size = declarator_object_size (base_size); - - if (object_size > 0) { - return object_size / declarator_first_array_count; - } - - } - - /* - * For a one-dimensional array, the size of the element reached by one - * subscript is the declared base object size, not the total flattened - * initializer field size. make_declarator_fields() expands objects like - * - * char parmbuf[410]; - * - * into 410 byte fields for initialization/copying purposes. Feeding that - * field total back here made parmbuf[i] scale by 410 and store a dword far - * beyond the stack object. - */ - 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 array_element_size = 0; - int final_pointer_depth = 0; - int final_pointed_size = 0; - int final_is_array = 0; - int final_array_element_size = 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; - array_element_size = local->array_element_size; - - } 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); - array_element_size = get_global_symbol_array_element_size (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; - final_array_element_size = array_element_size; - - 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; - final_array_element_size = member_is_array ? member_size : 0; - - 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_is_array && leading_stars > 0) { - - int remaining_stars = leading_stars - 1; - int elem_size = final_array_element_size > 0 ? final_array_element_size : - (final_pointer_depth > 0 ? DATA_PTR : final_pointed_size); - - /* - * In expressions, an array object decays to a pointer to its first - * element before the leading '*' operators are applied. For an - * array of pointers, sizeof(*array) is therefore pointer-sized, - * not the size of the pointed-to base type. - */ - size = elem_size > 0 ? elem_size : size; - - if (remaining_stars > 0 && final_pointer_depth >= remaining_stars) { - - int remaining_depth = final_pointer_depth - remaining_stars; - size = remaining_depth > 0 ? DATA_PTR : final_pointed_size; - - } - - final_is_array = 0; - final_pointer_depth = 0; - - } else if (final_pointer_depth > 0) { - - size = final_pointer_depth > 1 ? DATA_PTR : final_pointed_size; - final_pointer_depth--; - - } else if (final_is_array && final_array_element_size > 0) { - size = final_array_element_size; - } else if (final_is_array && final_pointed_size > 0) { - size = final_pointed_size; - } - - final_is_array = 0; - - } - - if (leading_stars > 0 && final_is_array) { - - int remaining_stars = leading_stars - 1; - int elem_size = final_array_element_size > 0 ? final_array_element_size : - (final_pointer_depth > 0 ? DATA_PTR : final_pointed_size); - - /* - * In an expression, an array object decays to a pointer to its first - * element before unary '*' is applied. This is needed for plain - * sizeof(*array) as well as for forms that later include subscripts. - * For example, with char *builtins[], sizeof(*builtins) is the size - * of one array element, i.e. pointer-sized, not sizeof(char). - */ - size = elem_size > 0 ? elem_size : size; - - if (remaining_stars > 0 && final_pointer_depth >= remaining_stars) { - - int remaining_depth = final_pointer_depth - remaining_stars; - size = remaining_depth > 0 ? DATA_PTR : final_pointed_size; - - } - - final_is_array = 0; - final_pointer_depth = 0; - - } 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; - int saved_declarator_array_dimensions = declarator_array_dimensions; - - long saved_declarator_array_count = declarator_array_count; - long saved_declarator_first_array_count = declarator_first_array_count; - - int size = DATA_INT & 0x1f, 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_first_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; - declarator_first_array_count = saved_declarator_first_array_count; - declarator_array_dimensions = saved_declarator_array_dimensions; - - 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 current_expression_mentions_64bit_symbol_now (void); -static int rhs_current_operand_is_unsigned_now (void); - -static int64_s parse_floating_const_expr_bits_now (int size); - -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, int object_size, int object_is_floating) { - - 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, object_size, object_is_floating); - - 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; - - } - - } - - if (object_is_floating) { - init->value = parse_floating_const_expr_bits_now (object_size); - } else { - 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], 0, 0); - (*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 (current_function_frame_deferred) { - - *block_stack_bytes = needed_stack_bytes; - *block_stack_emitted = (needed_stack_bytes > 0); - - 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]; - - int declaration_dllimport = parsed_dllimport; - - for (i = 0; i < MAX_AGG_FIELDS; i++) { - init_symbols[i] = 0; - } - - parse_declarator (&name); - - declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; - 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, - (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, - object_fields, object_field_count); - - } else if (name && (parsed_storage_class == STORAGE_EXTERN || declaration_dllimport)) { - - add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); - - if (find_global_symbol (name) >= 0) { - - set_global_symbol_size (name, (declarator_has_function && !declarator_is_pointer && !declarator_function_is_pointer) ? parsed_type_size : ((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 && declarator_function_is_pointer)) ? 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); - - set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); - set_global_symbol_dllimport (name, declaration_dllimport); - - copy_pending_params_to_global_symbol (name); - - } - - set_global_symbol_dllimport (name, declaration_dllimport); - 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); - set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); - 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 & 0x1f); - 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 || parsed_dllimport)) { - - 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], elem_size, 0); - } - - 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; - - if (current_local_stack_size > current_function_frame_size) { - current_function_frame_size = current_local_stack_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_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && is_string_token ()) { - - int first_init_index = init_count; - int value_count = 0; - - int64_s values[MAX_STRING_INIT_BYTES]; - int i; - - parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count); - - if (declarator_array_unsized && value_count > 0 && value_count > object_size) { - - long old_offset = object_offset; - long new_offset; - long delta; - - int adj_i; - current_local_stack_size += value_count - object_size; - - if (current_local_stack_size > current_function_frame_size) { - current_function_frame_size = current_local_stack_size; - } - - new_offset = -current_local_stack_size; - delta = new_offset - old_offset; - object_offset = new_offset; - declarator_array_count = value_count; - - 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; - } - - } - - for (i = 0; i < object_size; i++) { - - if (init_count >= MAX_LOCAL_INITS) { - continue; - } - - inits[init_count].offset = object_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++; - - } - - } 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 if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_has_array) { - - 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 (!declarator_is_pointer && parsed_type_is_floating) { - - emit_load_floating_rhs_expression_now (object_init_size); - emit_store_floating_to_local_now (object_offset, object_init_size); - - } 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], object_init_size, (!declarator_is_pointer && parsed_type_is_floating)); - } - - 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_dimensions (static_label, declarator_has_array ? declarator_array_dimensions : 0); - set_global_symbol_array_element_size (static_label, declarator_array_element_size_now (parsed_type_size)); - - 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); - set_local_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); - 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); - } - - } - - } - - 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; - - if (!current_function_frame_deferred) { - emit_stack_adjust (block_stack_bytes, 1); - } - - if (!is_function_body && !current_function_frame_deferred) { - 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; - - if (!current_function_frame_deferred) { - emit_stack_adjust (extra_stack_bytes, 1); - } - - if (!is_function_body && !current_function_frame_deferred) { - 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; - - if (!current_function_frame_deferred) { - emit_stack_adjust (block_stack_bytes, 1); - } - - block_stack_emitted = (block_stack_bytes > 0); - - } else if (needed_stack_bytes > block_stack_bytes) { - - if (!current_function_frame_deferred) { - 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 && !current_function_frame_deferred && 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_ex (const char *reg, long offset, int size, int is_unsigned) { - - char memref[64]; - size &= 0x1f; - - 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 ? " %s %s, byte %s\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", reg, memref); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word %s\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", 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, " %s %ld(%%ebp), %%%s\n", is_unsigned ? "movzbl" : "movsbl", offset, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s %ld(%%ebp), %%%s\n", is_unsigned ? "movzwl" : "movswl", offset, reg); - } else { - fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, reg); - } - - } - -} - -static void emit_load_local_to_reg (const char *reg, long offset, int size) { - emit_load_local_to_reg_ex (reg, offset, size, 0); -} - -static void emit_load_global_to_reg_ex (const char *reg, const char *symbol, int size, int is_unsigned) { - - const char *asm_symbol; - size &= 0x1f; - - if (!state->ofp || !reg || !symbol) { - return; - } - - emit_extern_reference_symbol (symbol, size); - - if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { - - asm_symbol = asm_global_import_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"), reg, asm_symbol); - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, byte [%s]\n" : " %s %s, byte ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", reg, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr [%s]\n"), is_unsigned ? "movzx" : "movsx", reg, reg); - } else { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr [%s]\n"), reg, reg); - } - - } else { - - fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg); - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", reg, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswl", reg, reg); - } else { - fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, 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 ? " %s %s, byte [%s]\n" : " %s %s, byte ptr %s\n"), is_unsigned ? "movzx" : "movsx", reg, asm_symbol); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s %s, word [%s]\n" : " %s %s, word ptr %s\n"), is_unsigned ? "movzx" : "movsx", 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, " %s %s, %%%s\n", is_unsigned ? "movzbl" : "movsbl", asm_symbol, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s %s, %%%s\n", is_unsigned ? "movzwl" : "movswl", asm_symbol, reg); - } else { - fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg); - } - - } - -} - -static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) { - emit_load_global_to_reg_ex (reg, symbol, size, 0); -} - -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; - } - - if (get_global_symbol_dllimport (symbol) && get_global_symbol_kind (symbol) == GLOBAL_SYMBOL_OBJECT) { - - const char *addr_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx"; - - asm_symbol = asm_global_import_symbol_name (symbol); - emit_extern_reference_symbol (symbol, size); - - 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"), addr_reg, asm_symbol); - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte [%s], %cl\n" : " mov byte ptr [%s], %cl\n"), addr_reg, 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"), addr_reg, reg[1]); - } else { - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr [%s], %s\n"), addr_reg, reg); - } - - } else { - - fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, addr_reg); - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, " movb %%%cl, (%%%s)\n", reg[1], addr_reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " movw %%%cx, (%%%s)\n", reg[1], addr_reg); - } else { - fprintf (state->ofp, " movl %%%s, (%%%s)\n", reg, addr_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, int is_unsigned) { - - 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, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); - } else { - fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); - } - - } else { - fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", base_reg, index_reg, dst_reg); - } - -} - -static void emit_load_indexed_sized_to_reg_ex_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size, int is_unsigned) { - - int scale = 1; - - const char *gasop = is_unsigned ? "movzbl" : "movsbl"; - const char *atype = "byte"; - - if (!state->ofp || !base_reg || !index_reg || !dst_reg) { - return; - } - - elem_size &= 0x1f; - - if (elem_size == (DATA_SHORT & 0x1f)) { - - scale = 2; - - atype = "word"; - gasop = "movswl"; - - } 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, is_unsigned); - } else if (scale == 1) { - - if (strcmp (atype, "byte") == 0) { - - if (state->syntax & ASM_SYNTAX_NASM) { - fprintf (state->ofp, " %s %s, byte [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); - } else { - fprintf (state->ofp, " %s %s, byte ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); - } - - } else if (elem_size == (DATA_SHORT & 0x1f)) { - - if (state->syntax & ASM_SYNTAX_NASM) { - fprintf (state->ofp, " %s %s, word [%s + %s]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg); - } else { - fprintf (state->ofp, " %s %s, word ptr [%s + %s]\n", is_unsigned ? "movzx" : "movsx", 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 (strcmp (atype, "byte") == 0) { - - if (state->syntax & ASM_SYNTAX_NASM) { - fprintf (state->ofp, " %s %s, byte [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); - } else { - fprintf (state->ofp, " %s %s, byte ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); - } - - } else if (elem_size == (DATA_SHORT & 0x1f)) { - - if (state->syntax & ASM_SYNTAX_NASM) { - fprintf (state->ofp, " %s %s, word [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", dst_reg, base_reg, index_reg, scale); - } else { - fprintf (state->ofp, " %s %s, word ptr [%s + %s * %d]\n", is_unsigned ? "movzx" : "movsx", 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_indexed_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { - emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 0); -} - -static void emit_load_indexed_unsigned_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) { - emit_load_indexed_sized_to_reg_ex_now (base_reg, index_reg, dst_reg, elem_size, 1); -} - -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 void emit_load_subscript_index_to_reg_now (const char *index_reg) { - - if (current_expression_mentions_64bit_symbol_now ()) { - - int is_unsigned = rhs_current_operand_is_unsigned_now (); - emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); - - if (index_reg && strcmp (index_reg, "eax") != 0) { - emit_mov_reg_to_reg_now (index_reg, "eax"); - } - - return; - - } - - emit_load_assignment_rhs_expression_to_reg (index_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_subscript_index_to_reg_now (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_subscript_index_to_reg_now (index_reg); - - expect (TOK_RBRACK, "]"); - emit_pop_reg_now (reg); - - if (tok.kind == TOK_LBRACK) { - - if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { - - /* - * This is a real multidimensional array row, not an array of - * pointers. For char a[7][3], a[i] is the address of the - * three-byte row. The old code treated every further subscript - * as pointer traversal and loaded *(a + i), which turns the - * first bytes of the row into a bogus pointer. - */ - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - elem_size = index_step_size (pointed_size); - - } else { - - 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 (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { - 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 emit_parse_postfix_subscripts_to_reg_dims_now (const char *reg, int elem_size, int pointer_depth, int pointed_size, int array_dimensions, int is_unsigned) { - - 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) { - - int dims_before = array_dimensions; - - saw_subscript = 1; - get_token (); - - emit_push_reg_now (reg); - emit_load_subscript_index_to_reg_now (index_reg); - - expect (TOK_RBRACK, "]"); - emit_pop_reg_now (reg); - - if (array_dimensions > 0) { - array_dimensions--; - } - - if (tok.kind == TOK_LBRACK) { - - if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { - - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - elem_size = index_step_size (pointed_size); - - } else { - - 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 (pointer_depth == 0 && dims_before > 1) { - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - } else if (pointer_depth == 0 && pointed_size > 0 && elem_size > pointed_size) { - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - } else if (index_step_size (elem_size) > (DATA_PTR & 0x1f)) { - emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size); - } else { - - if (is_unsigned) { - emit_load_indexed_unsigned_sized_to_reg_now (reg, index_reg, 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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); - } else { - emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); - } - - } else if (find_global_symbol (name) >= 0) { - emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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_ex (reg, sym->static_label, sym->size, sym->is_unsigned); - } else { - emit_load_local_to_reg_ex (reg, sym->offset, sym->size, sym->is_unsigned); - } - - } else if (find_global_symbol (name) >= 0) { - emit_load_global_to_reg_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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_ex (lo, sym->static_label, sym->size, sym->is_unsigned); - } else { - emit_load_local_to_reg_ex (lo, sym->offset, sym->size, sym->is_unsigned); - } - - 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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (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 fold_text_starts_with_type_name_only_before_rparen (const char *p) { - - int saw_type = 0; - char word[64]; - int i; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - for (;;) { - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '*') { - - saw_type = 1; - - p++; - continue; - - } - - if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) { - break; - } - - 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, "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_type = 1; - - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - return saw_type && *p == ')'; - -} - -static int const_integer_expr_text_is_foldable_now (const char *p) { - - int saw_value_token = 0; - int saw_token = 0; - - char word[64]; - int depth = 0, ch, i; - - if (!p) { - return 0; - } - - { - - const char *q = p; - int parens = 0; - - while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { - q++; - } - - while (*q == '(') { - - parens = 1; - q++; - - while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') { - q++; - } - - } - - if (fold_text_starts_with_type_name_only_before_rparen (p) || (parens && fold_text_starts_with_type_name_only_before_rparen (q))) { - 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) { - - /* - * If the caller asks about text that starts inside a cast, the - * scanner can see only the type-name prefix, for example: - * - * unsigned char) ch - * - * That is not an integer constant expression. Returning true - * here sends the parser down expr_const64(), which then reports - * "integer constant expression expected" at the cast. - */ - if (!saw_value_token) { - return 0; - } - - break; - - } - - depth--; - p++; - - continue; - - } - - if (*p >= '0' && *p <= '9') { - - saw_value_token = 1; - saw_token = 1; - - p++; - - while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { - p++; - } - - continue; - - } - - if (*p == '\'') { - - saw_value_token = 1; - 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) { - saw_value_token = 1; - } - - 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 int token_kind_is_integer_constant_now (enum token_kind kind) { - - return kind == TOK_CCHAR || kind == TOK_LCHAR || - kind == TOK_CINT || kind == TOK_CUINT || - kind == TOK_CLONG || kind == TOK_CULONG || - kind == TOK_CLLONG || kind == TOK_CULLONG; - -} - -static int token_kind_is_binary_expression_operator_now (enum token_kind kind) { - - return kind == TOK_PLUS || kind == TOK_MINUS || kind == TOK_STAR || - kind == TOK_FSLASH || kind == TOK_MOD || - kind == TOK_AMPER || kind == TOK_PIPE || kind == TOK_CARET || - kind == TOK_LSH || kind == TOK_RSH || - kind == TOK_LESS || kind == TOK_LTEQ || - kind == TOK_GREATER || kind == TOK_GTEQ || - kind == TOK_EQEQ || kind == TOK_NOTEQ || - kind == TOK_LOGAND || kind == TOK_LOGOR; - -} - -static int current_integer_expr_is_foldable_now (void) { - - struct token *saved_tok; - - enum token_kind first_kind; - enum token_kind next_kind; - - if (!const_integer_expr_text_is_foldable_now (tok.start)) { - return 0; - } - - /* - * Macro-expanded integer tokens can have tok.start/tok.caret pointing at - * only the replacement text (for example "16") rather than the complete - * source expression that follows it. Do not hand such a prefix to - * expr_const64() as though it described the whole expression when the real - * token stream continues with a binary operator: - * - * MEMMGR_ALIGN - (size_t)buffer % MEMMGR_ALIGN - * - * The normal expression parser can still handle this; this guard only - * disables the whole-expression constant-folder for that unsafe prefix. - */ - if (!token_kind_is_integer_constant_now (tok.kind)) { - return 1; - } - - first_kind = tok.kind; - - saved_tok = clone_current_token_now (); - get_token (); - - next_kind = tok.kind; - unget_token (saved_tok); - - if (token_kind_is_integer_constant_now (first_kind) && token_kind_is_binary_expression_operator_now (next_kind)) { - return 0; - } - - return 1; - -} - -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_ex_now (const char *reg, int size, int is_unsigned); -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_ex_now (const char *lo, const char *hi, int size, int is_unsigned) { - - 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_ex_now (lo, size, is_unsigned); - - 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 int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating); -static int rhs_current_operand_is_floating_now (void); -static int token_is_floating_constant_now (void); - -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 void emit_load_assignment_rhs_to_reg (const char *reg); -static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi); -static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size); - -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)) { - - if (!cast_is_pointer && !last_cast_type_is_floating && rhs_current_operand_is_floating_now ()) { - - emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); - - if (floating_rhs_result_in_eax_bool) { - - if (strcmp (lo, "eax") != 0 && state->ofp) { - - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " mov %s, eax\n", lo); - } else { - fprintf (state->ofp, " movl %%eax, %%%s\n", lo); - } - - } - - emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); - floating_rhs_result_in_eax_bool = 0; - - } else { - - emit_floating_stack_to_int_pair_now (lo, hi); - - if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { - emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned); - } - - } - - return; - - } - - 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 (tok.kind != TOK_LPAREN && current_integer_expr_is_foldable_now ()) { - - 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_LPAREN) { - - const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; - int cast_deref_size = DATA_INT & 0x1f; - - get_token (); - - if (is_type_start (tok.kind) && parse_deref_cast_type_name (&cast_deref_size)) { - - emit_load_assignment_rhs_to_reg (lo); - - if ((cast_deref_size & 0x1f) == (DATA_LLONG & 0x1f)) { - - emit_copy_reg_now (addr_reg, lo); - emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); - - } else { - - emit_load_deref_reg_now (lo, cast_deref_size); - emit_extend_pair_high_from_low (lo, hi, cast_deref_size, 1); - - } - - return; - - } - - emit_load_assignment_rhs_expression_to_reg (lo); - expect (TOK_RPAREN, ")"); - - emit_load_deref_reg_now (lo, DATA_INT & 0x1f); - emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1); - - return; - - } - - 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 (); - - src = find_local_symbol (name); - global_index = find_global_symbol (name); - - if (!src && global_index >= 0 && tok.kind == TOK_LPAREN && - get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) { - - const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; - int fptr_depth = get_global_symbol_pointer_depth (name); - int fpointed_size = get_global_symbol_pointed_size (name); - - if (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, lo, name_start, name_caret, name_line); - - if (fptr_depth > 1) { - deref_size = DATA_PTR & 0x1f; - } else if (fptr_depth == 1 && fpointed_size > 0) { - deref_size = fpointed_size & 0x1f; - } - - if (deref_size == (DATA_LLONG & 0x1f)) { - - emit_copy_reg_now (addr_reg, lo); - emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); - - } else { - - emit_load_deref_reg_now (lo, deref_size); - emit_extend_pair_high_from_low (lo, hi, deref_size, 1); - - } - - free (name); - return; - - } - - if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { - - postfix_incdec = 1; - postfix_op = tok.kind; - - get_token (); - - } - - 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_SCC_BUILTIN_VA_ARG) { - - int va_size = DATA_INT & 0x1f; - int va_unsigned = 1; - int va_pointer = 0; - int va_floating = 0; - - const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; - - get_token (); - emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); - - if (va_size == (DATA_LLONG & 0x1f) && va_pointer == 0) { - emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); - } else { - - emit_copy_reg_now (lo, addr_reg); - emit_load_deref_reg_now (lo, va_size); - - emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); - - } - - 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { - - int va_size = DATA_INT & 0x1f; - int va_unsigned = 1; - int va_pointer = 0; - int va_floating = 0; - - const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi"; - emit_parse_builtin_va_arg_address_to_reg_now (addr_reg, &va_size, &va_unsigned, &va_pointer, &va_floating); - - if (va_size == (DATA_LLONG & 0x1f) && !va_floating && va_pointer == 0) { - emit_load_pair_from_deref_reg_now (lo, hi, addr_reg); - } else { - - emit_copy_reg_now (lo, addr_reg); - emit_load_deref_reg_now (lo, va_size); - emit_extend_pair_high_from_low (lo, hi, va_size, va_unsigned); - - } - - free (name); - return; - - } - - 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 (get_global_function_returns_floating (name)) { - - if (state->ofp) { - - 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"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", lo); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp + 4]\n" : " mov %s, dword ptr [esp + 4]\n", hi); - fprintf (state->ofp, " add esp, 8\n"); - - } else { - - fprintf (state->ofp, " subl $8, %%esp\n"); - fprintf (state->ofp, " fstpl (%%esp)\n"); - fprintf (state->ofp, " movl (%%esp), %%%s\n", lo); - fprintf (state->ofp, " movl 4(%%esp), %%%s\n", hi); - fprintf (state->ofp, " addl $8, %%esp\n"); - - } - - } - - free (name); - return; - } - - 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 = src->tag_name; - - } 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 && (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_now ())) { - - emit_push_reg_now (lo); - emit_load_floating_rhs_expression_now (lvalue_size); - - emit_pop_reg_now (addr_reg); - emit_store_floating_member_to_addr_reg_now (addr_reg, 0, lvalue_size); - - free (name); - return; - } - - 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_ex_now (lo, hi, source_size, src ? (src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned) : (get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name))); - - 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_ex (lo, src->static_label, src->size, src->is_unsigned); - } else { - emit_load_local_to_reg_ex (lo, src->offset, src->size, src->is_unsigned); - } - - 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_ex (lo, name, get_global_symbol_size (name), get_global_symbol_unsigned (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; - - postfix_member_calling_convention = TOK_EOF; - - { - - 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; - postfix_member_calling_convention = last_found_member_calling_convention; - - 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 rhs_current_operand_is_floating_now (void); - -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 () || rhs_current_operand_is_floating_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_ex_now (const char *reg, int size, int is_unsigned) { - - const char *op8; - const char *op16; - - if (!state->ofp) { - return; - } - - op8 = is_unsigned ? "movzx" : "movsx"; - op16 = is_unsigned ? "movzx" : "movsx"; - - if (state->syntax & ASM_SYNTAX_INTEL) { - - if (state->syntax & ASM_SYNTAX_NASM) { - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, " %s %s, byte [%s]\n", op8, reg, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s %s, word [%s]\n", op16, reg, reg); - } else { - fprintf (state->ofp, " mov %s, dword [%s]\n", reg, reg); - } - - } else { - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, " %s %s, byte ptr [%s]\n", op8, reg, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s %s, word ptr [%s]\n", op16, reg, reg); - } else { - fprintf (state->ofp, " mov %s, dword ptr [%s]\n", reg, reg); - } - - } - - } else { - - if (size == (DATA_CHAR & 0x1f)) { - fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzbl" : "movsbl", reg, reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " %s (%%%s), %%%s\n", is_unsigned ? "movzwl" : "movswl", reg, reg); - } else { - fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, reg); - } - - } - -} - -static void emit_load_deref_reg_now (const char *reg, int size) { - emit_load_deref_reg_ex_now (reg, size, 0); -} - -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 pointed_is_unsigned) { - - 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_ex_now (reg, subscript_elem_size, pointer_depth <= 1 ? pointed_is_unsigned : 0); - - 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, " movsx %s, byte [%s + %d]\n", dst_reg, addr_reg, offset); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " movsx %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, " movsx %s, byte ptr [%s + %d]\n", dst_reg, addr_reg, offset); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " movsx %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, " movsbl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg); - } else if (size == (DATA_SHORT & 0x1f)) { - fprintf (state->ofp, " movswl %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); - - if (src->is_array && !src->pointer_depth) { - current_object_tag_name = src->tag_name; - } else if (!src->is_array && src->pointer_depth > 0) { - current_object_tag_name = src->pointed_tag_name; - } - - } 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 (get_global_symbol_array (src_name) && !get_global_symbol_pointer_depth (src_name)) { - current_object_tag_name = get_global_symbol_tag_name (src_name); - } else if (!get_global_symbol_array (src_name) && get_global_symbol_pointer_depth (src_name) > 0) { - current_object_tag_name = get_global_symbol_tag_name (src_name); - } - - } - - if (elem_size <= 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_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_add_const_to_reg_now (const char *reg, int offset) { - - if (!state->ofp || !reg || offset == 0) { - return; - } - - 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 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 void save_parse_type_state_now ( - - int *saved_type_size, - int *saved_storage_class, - int *saved_is_aggregate, - int *saved_is_void, - int *saved_is_unsigned, - int *saved_is_floating, - int *saved_has_tag, - int *saved_is_inline, - int *saved_field_count, - int saved_fields[MAX_AGG_FIELDS], - int *saved_declarator_is_pointer, - int *saved_declarator_pointer_depth, - int *saved_declarator_has_array, - int *saved_declarator_has_function, - int *saved_declarator_array_unsized, - int *saved_declarator_array_dimensions, - long *saved_declarator_array_count, - long *saved_declarator_first_array_count) { - - int i; - - *saved_type_size = parsed_type_size; - *saved_storage_class = parsed_storage_class; - *saved_is_aggregate = parsed_type_is_aggregate; - *saved_is_void = parsed_type_is_void; - *saved_is_unsigned = parsed_type_is_unsigned; - *saved_is_floating = parsed_type_is_floating; - *saved_has_tag = parsed_type_has_tag; - *saved_is_inline = parsed_type_is_inline; - *saved_field_count = parsed_field_count; - - for (i = 0; i < *saved_field_count && i < MAX_AGG_FIELDS; i++) { - saved_fields[i] = parsed_field_sizes[i]; - } - - *saved_declarator_is_pointer = declarator_is_pointer; - *saved_declarator_pointer_depth = declarator_pointer_depth; - *saved_declarator_has_array = declarator_has_array; - *saved_declarator_has_function = declarator_has_function; - *saved_declarator_array_unsized = declarator_array_unsized; - *saved_declarator_array_dimensions = declarator_array_dimensions; - *saved_declarator_array_count = declarator_array_count; - *saved_declarator_first_array_count = declarator_first_array_count; - -} - -static void restore_parse_type_state_now ( - int saved_type_size, - int saved_storage_class, - int saved_is_aggregate, - int saved_is_void, - int saved_is_unsigned, - int saved_is_floating, - int saved_has_tag, - int saved_is_inline, - int saved_field_count, - int saved_fields[MAX_AGG_FIELDS], - int saved_declarator_is_pointer, - int saved_declarator_pointer_depth, - int saved_declarator_has_array, - int saved_declarator_has_function, - int saved_declarator_array_unsized, - int saved_declarator_array_dimensions, - long saved_declarator_array_count, - long saved_declarator_first_array_count) { - - int i; - - 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_dimensions = saved_declarator_array_dimensions; - declarator_array_count = saved_declarator_array_count; - declarator_first_array_count = saved_declarator_first_array_count; - -} - -static int parse_builtin_va_arg_type_now (int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { - - int saved_type_size; - int saved_storage_class; - int saved_is_aggregate; - int saved_is_void; - int saved_is_unsigned; - int saved_is_floating; - int saved_has_tag; - int saved_is_inline; - int saved_field_count; - int saved_fields[MAX_AGG_FIELDS]; - int saved_declarator_is_pointer; - int saved_declarator_pointer_depth; - int saved_declarator_has_array; - int saved_declarator_has_function; - int saved_declarator_array_unsized; - int saved_declarator_array_dimensions; - - long saved_declarator_array_count; - long saved_declarator_first_array_count; - - char *type_name = 0; - - int base_size; - int pointer_depth; - int is_unsigned; - int is_floating; - - save_parse_type_state_now (&saved_type_size, &saved_storage_class, - &saved_is_aggregate, &saved_is_void, &saved_is_unsigned, - &saved_is_floating, &saved_has_tag, &saved_is_inline, - &saved_field_count, saved_fields, &saved_declarator_is_pointer, - &saved_declarator_pointer_depth, &saved_declarator_has_array, - &saved_declarator_has_function, &saved_declarator_array_unsized, - &saved_declarator_array_dimensions, &saved_declarator_array_count, - &saved_declarator_first_array_count); - - declarator_is_pointer = 0; - declarator_pointer_depth = 0; - declarator_has_array = 0; - declarator_has_function = 0; - declarator_array_unsized = 0; - declarator_array_count = 0; - declarator_first_array_count = 1; - declarator_array_dimensions = 0; - - parse_type_spec (); - base_size = parsed_type_size & 0x1f; - - is_unsigned = parsed_type_is_unsigned; - is_floating = parsed_type_is_floating; - - if (tok.kind != TOK_RPAREN) { - parse_declarator (&type_name); - } - - pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0; - - if (type_name) { - free (type_name); - } - - if (out_pointer) { - *out_pointer = pointer_depth; - } - - if (out_unsigned) { - *out_unsigned = is_unsigned; - } - - if (out_floating) { - *out_floating = is_floating; - } - - if (out_size) { - *out_size = pointer_depth > 0 ? (DATA_PTR & 0x1f) : base_size; - } - - restore_parse_type_state_now (saved_type_size, saved_storage_class, - saved_is_aggregate, saved_is_void, saved_is_unsigned, - saved_is_floating, saved_has_tag, saved_is_inline, - saved_field_count, saved_fields, saved_declarator_is_pointer, - saved_declarator_pointer_depth, saved_declarator_has_array, - saved_declarator_has_function, saved_declarator_array_unsized, - saved_declarator_array_dimensions, saved_declarator_array_count, - saved_declarator_first_array_count); - - return 1; - -} - -static int emit_parse_builtin_va_arg_address_to_reg_now (const char *reg, int *out_size, int *out_unsigned, int *out_pointer, int *out_floating) { - - char *name; - - const char *name_start; - const char *name_caret; - const char *scratch_reg; - - unsigned long name_line; - unsigned long inc; - - struct local_symbol *src; - - int global_index; - int size = DATA_INT & 0x1f; - int is_unsigned = 1; - int pointer_depth = 0; - int is_floating = 0; - int deref_lvalue = 0; - int paren_lvalue = 0; - - if (tok.kind != TOK_LPAREN) { - return 0; - } - - get_token (); - - if (tok.kind == TOK_LPAREN) { - - paren_lvalue = 1; - get_token (); - - } - - if (tok.kind == TOK_STAR) { - - deref_lvalue = 1; - 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 va_list object in __scc_builtin_va_arg"); - return 1; - - } - - name = xstrdup (tok.ident); - get_token (); - - if (paren_lvalue) { - expect (TOK_RPAREN, ")"); - } - - expect (TOK_COMMA, ","); - - if (!is_type_start (tok.kind)) { - - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type name in __scc_builtin_va_arg"); - - free (name); - return 1; - - } - - parse_builtin_va_arg_type_now (&size, &is_unsigned, &pointer_depth, &is_floating); - expect (TOK_RPAREN, ")"); - - if (size <= 0) { - size = DATA_INT & 0x1f; - } - - inc = (unsigned long) size; - - 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) { - - scratch_reg = strcmp (reg, "edx") == 0 ? "ecx" : "edx"; - - if (src) { - - if (src->is_static && src->static_label) { - emit_load_global_to_reg (scratch_reg, src->static_label, DATA_PTR); - } else { - emit_load_local_to_reg (scratch_reg, src->offset, DATA_PTR); - } - - } else { - emit_load_global_to_reg (scratch_reg, name, DATA_PTR); - } - - if (state->ofp) { - - 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", reg, scratch_reg); - fprintf (state->ofp, " push %s\n", reg); - fprintf (state->ofp, " add %s, %lu\n", reg, inc); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr [%s], %s\n", scratch_reg, reg); - fprintf (state->ofp, " pop %s\n", reg); - - } else { - - fprintf (state->ofp, " movl (%%%s), %%%s\n", scratch_reg, reg); - fprintf (state->ofp, " pushl %%%s\n", reg); - fprintf (state->ofp, " addl $%lu, %%%s\n", inc, reg); - fprintf (state->ofp, " movl %%%s, (%%%s)\n", reg, scratch_reg); - fprintf (state->ofp, " subl $%lu, %%%s\n", inc, reg); - fprintf (state->ofp, " popl %%%s\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, " push %s\n", reg); - fprintf (state->ofp, " add %s, %lu\n", reg, inc); - - } else { - - fprintf (state->ofp, " pushl %%%s\n", reg); - fprintf (state->ofp, " addl $%lu, %%%s\n", inc, 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 (state->ofp) { - - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " pop %s\n", reg); - } else { - fprintf (state->ofp, " popl %%%s\n", reg); - } - - } - - } - - if (out_size) { - *out_size = size; - } - - if (out_unsigned) { - *out_unsigned = is_unsigned; - } - - if (out_pointer) { - *out_pointer = pointer_depth; - } - - if (out_floating) { - *out_floating = is_floating; - } - - 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_scale_reg_by_const_now (const char *reg, int scale); - -static int source_starts_deref_assignment_at_now (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 == '=' && p[1] != '=') { - return 1; - } - - if ((p[0] == '+' || p[0] == '-' || p[0] == '*' || p[0] == '/' || - p[0] == '%' || p[0] == '&' || p[0] == '^' || p[0] == '|') && - p[1] == '=') { - return 1; - } - - if ((p[0] == '<' && p[1] == '<' && p[2] == '=') || - (p[0] == '>' && p[1] == '>' && p[2] == '=')) { - return 1; - } - - return 0; - -} - -static int emit_load_deref_assignment_expression_to_reg_now (const char *reg) { - - char *name; - - const char *name_start; - const char *name_caret; - - unsigned long name_line; - - struct local_symbol *sym; - int global_index; - - enum token_kind op; - - int pointer_depth = 0; - int pointed_size = DATA_INT & 0x1f; - int value_pointer_depth = 0; - int store_size = DATA_INT & 0x1f; - - if (tok.kind != TOK_STAR) { - return 0; - } - - if (!source_starts_deref_assignment_at_now (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 (!is_assignment_operator (tok.kind)) { - - free (name); - return 0; - - } - - op = tok.kind; - get_token (); - - sym = find_local_symbol (name); - global_index = find_global_symbol (name); - - if (sym) { - - pointer_depth = sym->pointer_depth; - pointed_size = sym->pointed_size; - - 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 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 ("ecx", 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; - - } - - value_pointer_depth = pointer_depth - 1; - - if (value_pointer_depth > 0) { - store_size = DATA_PTR & 0x1f; - } else if (pointed_size > 0) { - store_size = pointed_size & 0x1f; - } - - if (store_size <= 0) { - store_size = DATA_INT & 0x1f; - } - - emit_push_reg_now ("ecx"); - - if (op == TOK_ASSIGN) { - emit_load_assignment_rhs_expression_to_reg ("eax"); - } else { - - emit_load_deref_reg_now ("ecx", store_size); - - emit_push_reg_now ("ecx"); - emit_load_assignment_rhs_expression_to_reg ("edx"); - - emit_pop_reg_now ("eax"); - - if ((op == TOK_PLUSEQ || op == TOK_MINUSEQ) && value_pointer_depth > 0 && - index_step_size (pointed_size) > 1) { - emit_scale_reg_by_const_now ("edx", index_step_size (pointed_size)); - } - - emit_assignment_binary_op (op, 0); - - } - - emit_pop_reg_now ("ecx"); - emit_store_reg_to_deref_reg_now ("ecx", "eax", store_size); - - if (reg && 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); - } - - } - - expect (TOK_RPAREN, ")"); - - if (value_pointer_depth > 0) { - set_rhs_last_pointer_info (value_pointer_depth, pointed_size); - } else { - clear_rhs_last_pointer_info (); - } - - free (name); - return 1; - -} - -static int source_starts_double_deref_at_now (const char *p) { - - if (!p || *p != '*') { - return 0; - } - - p++; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - return *p == '*'; - -} - -static int current_floating_token_is_nonzero_now (void) { - - if (tok.kind == TOK_CFLOAT) { - - union { float f; unsigned long u; } v; - - v.u = 0; - v.f = tok.val.f; - - return (v.u & 0x7fffffffUL) != 0; - - } - - { - - union { double d; unsigned long u[2]; } v; - - v.u[0] = 0; - v.u[1] = 0; - - if (tok.kind == TOK_CLDOUBLE) { - v.d = tok.val.ld; - } else { - v.d = tok.val.d; - } - - return ((v.u[1] & 0x7fffffffUL) != 0 || v.u[0] != 0); - - } - -} - -static void emit_load_assignment_rhs_to_reg (const char *reg) { - - clear_rhs_last_pointer_info (); - - if (tok.kind == TOK_STAR && source_starts_double_deref_at_now (tok.caret)) { - - int deref_size; - int inner_pointer_depth; - int inner_pointed_size; - - get_token (); - emit_load_assignment_rhs_to_reg (reg); - - inner_pointer_depth = rhs_last_pointer_depth; - inner_pointed_size = rhs_last_pointed_size; - - if (inner_pointer_depth > 1) { - deref_size = DATA_PTR & 0x1f; - } else if (inner_pointer_depth == 1 && inner_pointed_size > 0) { - deref_size = inner_pointed_size & 0x1f; - } else { - deref_size = DATA_INT & 0x1f; - } - - emit_load_deref_reg_now (reg, deref_size); - - if (inner_pointer_depth > 1) { - set_rhs_last_pointer_info (inner_pointer_depth - 1, inner_pointed_size); - } else { - clear_rhs_last_pointer_info (); - } - - 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 (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; - - if (emit_load_deref_assignment_expression_to_reg_now (reg)) { - return; - } - - /* - * 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; - int saved_declarator_array_dimensions = declarator_array_dimensions; - - long saved_declarator_array_count = declarator_array_count; - long saved_declarator_first_array_count = declarator_first_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; - declarator_first_array_count = 1; - - 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; - declarator_first_array_count = saved_declarator_first_array_count; - declarator_array_dimensions = saved_declarator_array_dimensions; - - 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 (tok.kind != TOK_LPAREN && current_integer_expr_is_foldable_now ()) { - - 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 () || rhs_current_operand_is_floating_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 ? src->pointed_tag_name : src->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->is_array ? (src->tag_name ? src->tag_name : src->pointed_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; - int member_load_size = DATA_PTR & 0x1f; - - 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 > 0) { - member_load_size = DATA_PTR & 0x1f; - } else { - member_load_size = member_size; - } - - 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 && offset != 0) { - - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " add edx, %d\n", offset); - } else { - fprintf (state->ofp, " addl $%d, %%edx\n", offset); - } - - } - - 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, member_load_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 (member_size > (DATA_PTR & 0x1f) && member_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); - } - - } - - } else { - emit_load_member_from_addr_reg_now (reg, reg, offset, member_load_size); - } - - if (lhs_has_postfix) { - /* Already applied above only for assignment forms. */ - } - - if (member_pointer_depth > 0) { - set_rhs_last_pointer_info (member_pointer_depth, member_elem_size); - } else { - clear_rhs_last_pointer_info (); - } - - 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, lhs_sym->pointed_is_unsigned)) { - - 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), get_global_symbol_pointed_is_unsigned (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)) { - - /* - * 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; - - } - - /* - * parse_deref_cast_type_name() consumes only the cast type - * parentheses. The operand being cast is still the current - * token, e.g. the ptr in: - * - * *(size_t *)ptr + sizeof(size_t) - * - * Load that address expression before applying the outer - * unary '*'. Otherwise the caller sees the unconsumed - * identifier/operator and reports a false "expected )". - */ - emit_load_assignment_rhs_to_reg (reg); - 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 (is_assignment_operator (tok.kind)) { - - enum token_kind assign_op = tok.kind; - - emit_push_reg_now (reg); - get_token (); - - if (assign_op == TOK_ASSIGN) { - emit_load_assignment_rhs_expression_to_reg (reg); - } else { - - emit_load_deref_reg_now (reg, deref_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, 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; - int64_s v; - - size_t len = tok.ident ? strlen (tok.ident) : 0; - - v.low = current_floating_token_is_nonzero_now () ? 1 : 0; - v.high = 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_SCC_BUILTIN_VA_ARG) { - - int va_size = DATA_INT & 0x1f; - int va_unsigned = 1; - int va_pointer = 0; - int va_floating = 0; - - get_token (); - - emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); - emit_load_deref_reg_now (reg, va_size); - - if (va_pointer > 0) { - set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); - } else { - - emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); - clear_rhs_last_pointer_info (); - - } - - 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 (); - - struct local_symbol *src; - int postfix_incdec = 0; - - get_token (); - - if (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { - - int va_size = DATA_INT & 0x1f; - int va_unsigned = 1; - int va_pointer = 0; - int va_floating = 0; - - emit_parse_builtin_va_arg_address_to_reg_now (reg, &va_size, &va_unsigned, &va_pointer, &va_floating); - emit_load_deref_reg_now (reg, va_size); - - if (va_pointer > 0) { - set_rhs_last_pointer_info (va_pointer, DATA_INT & 0x1f); - } else { - - emit_apply_integer_cast_to_reg_now (reg, va_size, va_unsigned); - clear_rhs_last_pointer_info (); - - } - - free (name); - return; - - } - - { - - 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 saw_subscript; - 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 && src->pointer_depth == 0 && elem_size <= (DATA_PTR & 0x1f)) { - - int aggregate_elem_size = aggregate_tag_size_or_zero (src->tag_name ? src->tag_name : src->pointed_tag_name); - - if (aggregate_elem_size > (DATA_PTR & 0x1f)) { - elem_size = aggregate_elem_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); - } - - saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, src->pointer_depth, src->pointed_size, src->array_dimensions, src->pointer_depth > 0 ? src->pointed_is_unsigned : src->is_unsigned); - - postfix_copy_lvalue_size = index_step_size (elem_size); - postfix_copy_lvalue_tag_name = src->is_array ? (src->tag_name ? src->tag_name : src->pointed_tag_name) : src->pointed_tag_name; - - } - - emit_apply_postfix_member_access_to_reg_now (reg); - postfix_copy_lvalue_tag_name = 0; - - 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { - - postfix_member_seen = 1; - postfix_member_pointer_depth = 0; - postfix_member_pointed_size = elem_size; - postfix_member_size = elem_size; - postfix_member_is_floating = src->is_floating; - postfix_member_is_unsigned = src->is_unsigned; - - } - - 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 saw_subscript; - int elem_size; - - { - - 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 (get_global_symbol_array (name) && get_global_symbol_pointer_depth (name) == 0 && elem_size <= (DATA_PTR & 0x1f)) { - - int aggregate_elem_size = aggregate_tag_size_or_zero (get_global_symbol_tag_name (name)); - - if (aggregate_elem_size > (DATA_PTR & 0x1f)) { - elem_size = aggregate_elem_size; - } - - } - - 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); - } - - saw_subscript = emit_parse_postfix_subscripts_to_reg_dims_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name), get_global_symbol_array_dimensions (name), get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_is_unsigned (name) : get_global_symbol_unsigned (name)); - - postfix_copy_lvalue_size = index_step_size (elem_size); - postfix_copy_lvalue_tag_name = get_global_symbol_array (name) ? get_global_symbol_tag_name (name) : 0; - - } - - emit_apply_postfix_member_access_to_reg_now (reg); - postfix_copy_lvalue_tag_name = 0; - - 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 && saw_subscript && elem_size > (DATA_PTR & 0x1f)) { - - postfix_member_seen = 1; - postfix_member_pointer_depth = 0; - postfix_member_pointed_size = elem_size; - postfix_member_size = elem_size; - postfix_member_is_floating = get_global_symbol_floating (name); - postfix_member_is_unsigned = get_global_symbol_unsigned (name); - - } - - 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_ex (reg, dst->static_label, dst->size, dst->is_unsigned); - } else { - emit_load_local_to_reg_ex (reg, dst->offset, dst->size, dst->is_unsigned); - } - - } else { - emit_load_global_to_reg_ex (reg, name, dst_size, get_global_symbol_unsigned (name)); - } - - 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_ex (reg, src->static_label, src->size, src->is_unsigned); - } else { - emit_load_local_to_reg_ex (reg, src->offset, src->size, src->is_unsigned); - } - - 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_ex (reg, name, get_global_symbol_size (name), get_global_symbol_unsigned (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 = get_global_symbol_tag_name (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 (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 = (double) tok.val.ld; - } else { - f = 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 double floating_constant_to_ld_now (void) { - - double v; - - if (tok.kind == TOK_CFLOAT) { - v = (float) tok.val.f; - } else if (tok.kind == TOK_CLDOUBLE) { - v = (double) tok.val.ld; - } else { - v = tok.val.d; - } - - get_token (); - return v; - -} - -static double int64_u32_base_now (void) { - - volatile unsigned long half; - double d; - - /* - * Build 2^32 without a direct large floating literal. The volatile word - * prevents the self compiler from folding this back into an LC*_flt data - * constant while compiling parse.c. - */ - half = 65536UL; - - d = (double) half; - d *= (double) half; - - return d; - -} - -static double int64_to_double_now (int64_s v) { - - double d; - - d = (double) v.high; - d *= int64_u32_base_now (); - d += (double) v.low; - - return d; - -} - -static double parse_floating_const_expr_value_now (void); -static double parse_floating_const_term_now (void); -static double parse_floating_const_primary_now (void); - -static double parse_floating_const_primary_now (void) { - - 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 (); - return int64_to_double_now (iv); - -} - -static double parse_floating_const_term_now (void) { - - double rhs, v; - enum token_kind op; - - 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 double parse_floating_const_expr_value_now (void) { - - double rhs, v; - enum token_kind op; - - 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) { - - 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; - - } - - { - - unsigned char bytes[8]; - - double d = (double) acc; - int i; - - 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) { - - switch_section (SECTION_DATA); - - if (size == (DATA_DOUBLE & 0x1f)) { - - /* - * Emit the exact IEEE bits as a 64-bit hex integer. The old - * decimal concatenated the high and low dwords as text, and the - * high==0 branch emitted only a single dd even though the later - * load is fld qword ptr. Both forms are unstable across - * bootstrap runs. - */ - fprintf (state->ofp, "LC%d_flt dq 0%08lX%08lXh\n", lab, v.high & U32_MASK, v.low & U32_MASK); - - } else { - fprintf (state->ofp, "LC%d_flt dd 0%08lXh\n", lab, v.low & U32_MASK); - } - - switch_section (SECTION_TEXT); - fprintf (state->ofp, " fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); - - } else { - - if (state->syntax & ASM_SYNTAX_NASM) { - - switch_section (SECTION_DATA); - - 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); - } - - switch_section (SECTION_TEXT); - fprintf (state->ofp, " fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab); - - } else { - - switch_section (SECTION_DATA); - - 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); - } - - switch_section (SECTION_TEXT); - - 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_ex ("eax", src->static_label, src->size, src->is_unsigned); - } else { - emit_load_local_to_reg_ex ("eax", src->offset, src->size, src->is_unsigned); - } - - } else { - emit_load_global_to_reg_ex ("eax", name, size, get_global_symbol_unsigned (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"); - - } - -} - -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 int emit_statement_rhs_const32_to_edx_if_possible (void); - -static void emit_statement_label (int label); -static void emit_statement_label_raw (int label); -static void emit_statement_jump (int label); - -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_fild_eax_now (void) { - - 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 void emit_load_any_deref_as_floating_now (const char *reg, int size, int is_floating) { - - if (is_floating) { - - emit_load_floating_deref_reg_now (reg, size); - return; - - } - - emit_load_deref_reg_now (reg, size); - emit_fild_eax_now (); - -} - -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; - int deref_is_floating = 1; - - get_token (); - - if (tok.kind == TOK_LPAREN) { - - get_token (); - - if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) { - - emit_load_any_deref_as_floating_now ("eax", deref_size, last_deref_cast_type_is_floating); - return; - - } - - emit_load_assignment_rhs_expression_to_reg ("eax"); - expect (TOK_RPAREN, ")"); - - if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { - - deref_size = rhs_last_pointed_size; - deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); - - } - - emit_load_any_deref_as_floating_now ("eax", deref_size, deref_is_floating); - return; - - } - - emit_load_assignment_rhs_to_reg ("eax"); - - if (rhs_last_pointer_depth > 0 && rhs_last_pointed_size > 0) { - - deref_size = rhs_last_pointed_size; - deref_is_floating = (deref_size == result_size && (result_size == (DATA_FLOAT & 0x1f) || result_size == (DATA_DOUBLE & 0x1f))); - - } - - emit_load_any_deref_as_floating_now ("eax", deref_size, deref_is_floating); - 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)) { - - if (!cast_is_pointer && !last_cast_type_is_floating) { - - if (rhs_current_operand_is_floating_now ()) { - - emit_load_floating_rhs_operand_now (result_size); - - if (floating_rhs_result_in_eax_bool) { - - emit_extend_pair_high_from_low ("eax", "edx", cast_size, cast_is_unsigned); - floating_rhs_result_in_eax_bool = 0; - - } else { - emit_floating_stack_to_int_pair_now ("eax", "edx"); - } - - if ((cast_size & 0x1f) != (DATA_LLONG & 0x1f)) { - emit_extend_pair_high_from_low ("eax", "edx", cast_size, cast_is_unsigned); - } - - } else { - emit_load_assignment_rhs_to_pair ("eax", "edx"); - } - - emit_integer_pair_to_floating_stack_now ("eax", "edx", cast_size); - return; - - } - - 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_SCC_BUILTIN_VA_ARG) { - - int va_size = DATA_DOUBLE & 0x1f; - int va_unsigned = 0; - int va_pointer = 0; - int va_floating = 0; - - get_token (); - - emit_parse_builtin_va_arg_address_to_reg_now ("eax", &va_size, &va_unsigned, &va_pointer, &va_floating); - emit_load_floating_deref_reg_now ("eax", va_size); - - 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 (strcmp (name, "__scc_builtin_va_arg") == 0 && tok.kind == TOK_LPAREN) { - - int va_size = DATA_DOUBLE & 0x1f; - int va_unsigned = 0; - int va_pointer = 0; - int va_floating = 0; - - emit_parse_builtin_va_arg_address_to_reg_now ("eax", &va_size, &va_unsigned, &va_pointer, &va_floating); - emit_load_floating_deref_reg_now ("eax", va_size); - - free (name); - return; - - } - - 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_function_returns_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_LBRACK && (src || find_global_symbol (name) >= 0)) { - - int elem_size; - int pointer_depth; - int pointed_size; - - 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); - - pointer_depth = src->pointer_depth; - pointed_size = src->pointed_size; - - if (src->is_array) { - - if (src->is_static && src->static_label) { - emit_load_symbol_address_to_reg_now ("eax", src->static_label, 0, 0); - } else { - emit_load_symbol_address_to_reg_now ("eax", 0, src->offset, 1); - } - - } else 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 { - - 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)); - - pointer_depth = get_global_symbol_pointer_depth (name); - pointed_size = get_global_symbol_pointed_size (name); - - if (get_global_symbol_array (name)) { - emit_load_symbol_address_to_reg_now ("eax", name, 0, 0); - } else { - emit_load_global_to_reg ("eax", name, DATA_PTR); - } - - } - - if (elem_size <= 0) { - elem_size = DATA_INT & 0x1f; - } - - emit_parse_postfix_subscripts_to_reg_now ("eax", elem_size, pointer_depth, pointed_size); - - if ((elem_size & 0x1f) > DATA_PTR) { - emit_load_floating_deref_reg_now ("eax", elem_size); - } else { - emit_fild_eax_now (); - } - - 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; - const char *current_tag_name; - - 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 current_size; - - if (src) { - - current_size = src->size; - current_tag_name = src->tag_name; - - } else { - - current_size = get_global_symbol_size (name); - current_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, current_size, current_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 (tok.kind != TOK_DOT && tok.kind != TOK_ARROW && member_is_floating) { - - emit_load_floating_member_symbol_now (src, name, member_offset, member_size); - - free (name); - return; - - } - - emit_load_symbol_address_for_copy_now ("eax", src, name); - emit_add_const_to_reg_now ("eax", member_offset); - - current_tag_name = last_found_member_tag_name; - - if (member_pointer_depth > 0 || member_is_array) { - current_size = member_elem_size; - } else { - current_size = member_size; - } - - while (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) { - - enum token_kind 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; - - } - - member = xstrdup (tok.ident); - get_token (); - - if (!find_member_info_ex_bounded (member, current_size, current_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 (member_op == TOK_ARROW) { - emit_load_deref_reg_now ("eax", DATA_PTR & 0x1f); - } - - emit_add_const_to_reg_now ("eax", member_offset); - - current_tag_name = last_found_member_tag_name; - - if (member_pointer_depth > 0 || member_is_array) { - current_size = member_elem_size; - } else { - current_size = member_size; - } - - } - - if (member_is_floating) { - emit_load_floating_member_from_addr_reg_now ("eax", 0, member_size); - } else { - - emit_load_deref_reg_now ("eax", member_size); - 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_ex ("eax", src->static_label, src->size, src->is_unsigned); - } else { - emit_load_local_to_reg_ex ("eax", src->offset, src->size, src->is_unsigned); - } - - 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_ex ("eax", name, get_global_symbol_size (name), get_global_symbol_unsigned (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) { - - if (!state->ofp) { - return; - } - - switch (k) { - - case TOK_PLUS: case TOK_PLUSEQ: - - fprintf (state->ofp, " faddp\n"); - break; - - case TOK_MINUS: case TOK_MINUSEQ: - - fprintf (state->ofp, " fsubrp\n"); - break; - - case TOK_STAR: case TOK_STAREQ: - - fprintf (state->ofp, " fmulp\n"); - break; - - case TOK_BSLASH: case TOK_SLASHEQ: - - fprintf (state->ofp, " fdivrp\n"); - 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 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) { - - enum token_kind saved_calling_convention = postfix_member_calling_convention; - - 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 = scc_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 **) xrealloc (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) { - - scc_close (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); - } - - scc_close (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"); - - if (saved_calling_convention == TOK_STDCALL) { - fprintf (state->ofp, " add esp, %d\n", (DATA_PTR & 0x1f)); - } else { - 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"); - - if (saved_calling_convention == TOK_STDCALL) { - fprintf (state->ofp, " addl $%d, %%esp\n", (DATA_PTR & 0x1f)); - } else { - 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]) { - scc_close (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 int current_argument_is_bare_64bit_identifier_now (void) { - - struct local_symbol *sym; - - if (tok.kind != TOK_IDENT || !tok.ident || !current_argument_is_bare_identifier_now ()) { - return 0; - } - - sym = find_local_symbol (tok.ident); - - if (sym) { - return sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating; - } - - if (find_global_symbol (tok.ident) >= 0) { - return get_global_symbol_size (tok.ident) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (tok.ident); - } - - return 0; - -} - -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_is_floating, ch, i; - int arg_bytes; - - int saved_arg_assignment32_stop_before_condition_operator; - int saved_arg_assignment64_stop_before_condition_operator; - - 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_saved_ofp = 0; - FILE *arg_tmp_ofp = 0; - FILE **arg_tmp_ofps = 0; - FILE **new_arg_tmp_ofps = 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 = scc_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); - } - - } - - } - - } - - saved_arg_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; - saved_arg_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; - - assignment32_stop_before_condition_operator = 0; - assignment64_stop_before_condition_operator = 0; - - 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 = scc_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 () && !find_local_symbol (tok.ident) && 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 (!use_inline && get_global_symbol_has_prototype (name) && - argc < get_global_symbol_param_count (name) && - get_global_symbol_param_size (name, argc) == (DATA_LLONG & 0x1f) && - !get_global_symbol_param_floating (name, argc)) { - - arg_is_floating = 0; - arg_bytes = DATA_LLONG & 0x1f; - - emit_load_assignment_rhs_expression_to_pair ("eax", "edx", get_global_symbol_param_unsigned (name, argc)); - - } else if (!use_inline && get_global_symbol_has_prototype (name) && get_global_symbol_is_variadic (name) && argc >= get_global_symbol_param_count (name) && current_argument_is_bare_64bit_identifier_now ()) { - - arg_is_floating = 0; - arg_bytes = DATA_LLONG & 0x1f; - - emit_load_assignment_rhs_expression_to_pair ("eax", "edx", rhs_current_operand_is_unsigned_now ()); - - } else 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 && arg_bytes == (DATA_LLONG & 0x1f)) { - - emit_push_reg_now ("edx"); - emit_push_reg_now ("eax"); - - } else 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 **) xrealloc (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) { - - scc_close (arg_tmp_ofp); - arg_tmp_ofp = 0; - - } - - argc++; - - if (!_accept (TOK_COMMA)) { - break; - } - - } - - } - - expect (TOK_RPAREN, ")"); - - assignment32_stop_before_condition_operator = saved_arg_assignment32_stop_before_condition_operator; - assignment64_stop_before_condition_operator = saved_arg_assignment64_stop_before_condition_operator; - - 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]) { - scc_close (arg_tmp_ofps[i]); - } - - } - - free (arg_tmp_ofps); - arg_tmp_ofps = 0; - - } - - 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) { - - if (arg_tmp_ofps) { - - for (i = argc - 1; i >= 0; i--) { - - if (arg_tmp_ofps[i]) { - - fseek (arg_tmp_ofps[i], 0, SEEK_SET); - - while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) { - fputc (ch, state->ofp); - } - - scc_close (arg_tmp_ofps[i]); - arg_tmp_ofps[i] = 0; - - } - - } - - 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 if (get_global_symbol_dllimport (name)) { - - remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); - fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " call dword [%s]\n" : " call dword ptr %s\n"), asm_global_import_symbol_name (name)); - - } else { - fprintf (state->ofp, " call %s\n", asm_name); - } - - if (total_arg_bytes > 0 && get_global_symbol_calling_convention (name) != TOK_STDCALL) { - 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 if (get_global_symbol_dllimport (name)) { - - remember_global_symbol_import_call_stack_bytes (name, total_arg_bytes); - fprintf (state->ofp, " call *%s\n", asm_global_import_symbol_name (name)); - - } else { - fprintf (state->ofp, " call %s\n", asm_name); - } - - if (total_arg_bytes > 0 && get_global_symbol_calling_convention (name) != TOK_STDCALL) { - 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]) { - scc_close (arg_tmp_ofps[i]); - } - - } - - free (arg_tmp_ofps); - arg_tmp_ofps = 0; - - } - -} - -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 = 0; - -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; - - /* - * With a single fixed function frame, block-local symbols still have - * different logical stack depths, but ESP no longer changes when - * entering/leaving those blocks. The old trampoline fixup would - * therefore corrupt ESP before the real jump target. - */ - if (!current_function_uses_single_frame) { - - 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 = scc_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); - - scc_close (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) { - - switch (k) { - - case TOK_LESS: - case TOK_LTEQ: - case TOK_GREATER: - case TOK_GTEQ: - case TOK_EQEQ: - case TOK_NOTEQ: - - return 1; - - default: - - return 0; - - } - -} - -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) { - - 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 (current_integer_expr_is_foldable_now ()) { - - 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_statement_const32_to_edx (int64_s v); - -static int is_assignment32_condition_stop_operator (enum token_kind k) { - - /* - * Keep this as a switch rather than a chained || expression. This guard - * protects statement-condition parsing from consuming compare/logical - * operators too early; if a self-built stage miscompiles the || chain - * here, ordinary conditions can degrade into: - * - * mov eax, - * test eax, eax - */ - switch (k) { - - case TOK_LESS: - case TOK_LTEQ: - case TOK_GREATER: - case TOK_GTEQ: - case TOK_EQEQ: - case TOK_NOTEQ: - case TOK_LOGAND: - case TOK_LOGOR: - - return 1; - - default: - - return 0; - - } - -} - -static void emit_load_assignment_compare_expression_to_reg (const char *reg) { - - int64_s rhs_enum_value; - enum token_kind op; - - int lhs_pointer_depth; - int is_unsigned; - int rhs_is_enum; - - 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 (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { - return; - } - - if (is_value_compare_operator (tok.kind)) { - - op = tok.kind; - get_token (); - - rhs_is_enum = 0; - - if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &rhs_enum_value)) { - - rhs_is_enum = 1; - 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); - } - - } - - /* - * Keep identifier-vs-enum comparisons structurally as a real compare. - * During bootstrap this path is hit when compiling ordinary tests such - * as: - * - * unary_op == TOK_MINUS - * - * If the RHS enum is allowed to go through the generic expression loader, - * a later self-built compiler can collapse the expression into just: - * - * mov eax, 45 - * test eax, eax - * - * which makes every non-zero enum comparison true. Loading the enum - * directly into EDX avoids clobbering the already-loaded LHS in EAX and - * avoids the fragile push/pop/generic-RHS sequence entirely. - */ - if (rhs_is_enum) { - - emit_statement_const32_to_edx (rhs_enum_value); - - if (lhs_pointer_depth > 0) { - is_unsigned = 1; - } - - emit_compare_eax_edx_to_reg (op, reg, is_unsigned); - return; - - } - - emit_push_reg_now ("eax"); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - 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; - } - - /* - * C requires floating-to-integer conversion to discard the fractional - * part. x87 fistp uses the current FPU rounding mode, which is normally - * round-to-nearest, so values such as 0.5000000001 become 1 instead of 0. - * Temporarily switch the x87 control word to truncate for this conversion. - */ - if (state->syntax & ASM_SYNTAX_INTEL) { - - fprintf (state->ofp, " sub esp, 8\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [esp + 4]\n" : " fnstcw word ptr [esp + 4]\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [esp + 4]\n" : " mov ax, word ptr [esp + 4]\n"); - fprintf (state->ofp, " or ah, 12\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [esp + 6], ax\n" : " mov word ptr [esp + 6], ax\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 6]\n" : " fldcw word ptr [esp + 6]\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 ? " fldcw word [esp + 4]\n" : " fldcw word ptr [esp + 4]\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, 8\n"); - - } else { - - fprintf (state->ofp, " subl $8, %%esp\n"); - fprintf (state->ofp, " fnstcw 4(%%esp)\n"); - fprintf (state->ofp, " movw 4(%%esp), %%ax\n"); - fprintf (state->ofp, " orb $12, %%ah\n"); - fprintf (state->ofp, " movw %%ax, 6(%%esp)\n"); - fprintf (state->ofp, " fldcw 6(%%esp)\n"); - fprintf (state->ofp, " fistpl (%%esp)\n"); - fprintf (state->ofp, " fldcw 4(%%esp)\n"); - fprintf (state->ofp, " movl (%%esp), %%%s\n", reg); - fprintf (state->ofp, " addl $8, %%esp\n"); - - } - -} - -static void emit_floating_stack_to_int_pair_now (const char *lo, const char *hi) { - - if (!state->ofp) { - return; - } - - if (state->syntax & ASM_SYNTAX_INTEL) { - - fprintf (state->ofp, " sub esp, 12\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fnstcw word [esp + 8]\n" : " fnstcw word ptr [esp + 8]\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ax, word [esp + 8]\n" : " mov ax, word ptr [esp + 8]\n"); - fprintf (state->ofp, " or ah, 12\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [esp + 10], ax\n" : " mov word ptr [esp + 10], ax\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 10]\n" : " fldcw word ptr [esp + 10]\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp qword [esp]\n" : " fistp qword ptr [esp]\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fldcw word [esp + 8]\n" : " fldcw word ptr [esp + 8]\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", lo); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp + 4]\n" : " mov %s, dword ptr [esp + 4]\n", hi); - fprintf (state->ofp, " add esp, 12\n"); - - } else { - - fprintf (state->ofp, " subl $12, %%esp\n"); - fprintf (state->ofp, " fnstcw 8(%%esp)\n"); - fprintf (state->ofp, " movw 8(%%esp), %%ax\n"); - fprintf (state->ofp, " orb $12, %%ah\n"); - fprintf (state->ofp, " movw %%ax, 10(%%esp)\n"); - fprintf (state->ofp, " fldcw 10(%%esp)\n"); - fprintf (state->ofp, " fistpll (%%esp)\n"); - fprintf (state->ofp, " fldcw 8(%%esp)\n"); - fprintf (state->ofp, " movl (%%esp), %%%s\n", lo); - fprintf (state->ofp, " movl 4(%%esp), %%%s\n", hi); - fprintf (state->ofp, " addl $12, %%esp\n"); - - } - -} - -static void emit_integer_pair_to_floating_stack_now (const char *lo, const char *hi, int size) { - - if (!state->ofp) { - return; - } - - if ((size & 0x1f) == (DATA_LLONG & 0x1f)) { - - if (state->syntax & ASM_SYNTAX_INTEL) { - - fprintf (state->ofp, " sub esp, 8\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp], %s\n" : " mov dword ptr [esp], %s\n", lo); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp + 4], %s\n" : " mov dword ptr [esp + 4], %s\n", hi); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild qword [esp]\n" : " fild qword ptr [esp]\n"); - fprintf (state->ofp, " add esp, 8\n"); - - } else { - - fprintf (state->ofp, " subl $8, %%esp\n"); - fprintf (state->ofp, " movl %%%s, (%%esp)\n", lo); - fprintf (state->ofp, " movl %%%s, 4(%%esp)\n", hi); - fprintf (state->ofp, " fildll (%%esp)\n"); - fprintf (state->ofp, " addl $8, %%esp\n"); - - } - - return; - - } - - if (state->syntax & ASM_SYNTAX_INTEL) { - - fprintf (state->ofp, " sub esp, 4\n"); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp], %s\n" : " mov dword ptr [esp], %s\n", lo); - fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fild dword [esp]\n" : " fild dword ptr [esp]\n"); - fprintf (state->ofp, " add esp, 4\n"); - - } else { - - fprintf (state->ofp, " subl $4, %%esp\n"); - fprintf (state->ofp, " movl %%%s, (%%esp)\n", lo); - fprintf (state->ofp, " fildl (%%esp)\n"); - 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; - - if (rhs_current_operand_is_floating_now ()) { - - emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f); - - /* - * Floating comparisons leave their boolean result in eax. - * Do not run that through fistp: emit_floating_compare_to_eax_now() - * has already consumed both x87 operands with fcompp, so the x87 - * stack no longer contains a value to convert. - */ - if (floating_rhs_result_in_eax_bool) { - - if (state->ofp && 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); - } - - } - - floating_rhs_result_in_eax_bool = 0; - return; - - } - - emit_floating_stack_to_int_reg_now (reg); - return; - - } - - emit_load_assignment_compare_expression_to_reg (reg); - - for (;;) { - - if (assignment32_stop_before_condition_operator && is_assignment32_condition_stop_operator (tok.kind)) { - break; - } - - if (tok.kind == TOK_LOGAND) { - - false_label = anon_label++; - true_label = anon_label++; - end_label = anon_label++; - - get_token (); - - emit_test_reg_jump_zero_now (reg, false_label); - emit_load_assignment_compare_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); - - continue; - - } - - if (tok.kind == TOK_LOGOR) { - - false_label = anon_label++; - true_label = anon_label++; - end_label = anon_label++; - - get_token (); - - emit_test_reg_jump_nonzero_now (reg, true_label); - emit_load_assignment_compare_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); - - continue; - - } - - break; - - } - - 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_condition_stop_operator (enum token_kind k) { - - return 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 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 (current_integer_expr_is_foldable_now ()) { - - 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) && !(assignment64_stop_before_condition_operator && is_assignment64_condition_stop_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 (tok.kind == TOK_INCR || tok.kind == TOK_DECR) { - - if (emit_load_prefix_incdec_member_to_reg_now ("eax")) { - - expect_semi_or_recover (); - return 1; - - } - - } - - 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; - int saved_declarator_array_dimensions = declarator_array_dimensions; - - long saved_declarator_first_array_count = declarator_first_array_count; - 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; - declarator_first_array_count = saved_declarator_first_array_count; - declarator_array_dimensions = saved_declarator_array_dimensions; - - 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 lparen_expression_starts_with_star_now (void) { - - const char *p; - - if (tok.kind != TOK_LPAREN || !tok.caret) { - return 0; - } - - p = tok.caret + 1; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - return *p == '*'; - -} - -static int parse_parenthesized_indirect_assignment_statement (void) { - - enum token_kind op; - - int pointer_depth; - int pointed_size; - int deref_size = DATA_INT & 0x1f; - - if (tok.kind != TOK_LPAREN) { - return 0; - } - - get_token (); - emit_load_assignment_rhs_expression_to_reg ("edx"); - - pointer_depth = rhs_last_pointer_depth; - pointed_size = rhs_last_pointed_size; - - if (pointer_depth > 1) { - deref_size = DATA_PTR & 0x1f; - } else if (pointer_depth == 1 && pointed_size > 0) { - deref_size = pointed_size & 0x1f; - } - - if (deref_size <= 0) { - deref_size = DATA_INT & 0x1f; - } - - expect (TOK_RPAREN, ")"); - - if (!is_assignment_operator (tok.kind)) { - - 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 deref_size = DATA_INT & 0x1f; - int deref_is_floating = 0; - - enum token_kind op; - int global_index; - - 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 (); - } - - if (lparen_expression_starts_with_star_now ()) { - return parse_parenthesized_pointer_member_indirect_assignment_statement (); - } - - return parse_parenthesized_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; - deref_is_floating = 0; - - } 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; - deref_is_floating = lhs->pointed_is_floating; - - } - - } else { - - if (get_global_symbol_pointer_depth (name) > 1) { - - deref_size = DATA_PTR & 0x1f; - deref_is_floating = 0; - - } else if (get_global_symbol_pointer_depth (name) == 1) { - - deref_size = get_global_symbol_pointed_size (name); - deref_is_floating = get_global_symbol_pointed_is_floating (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_is_floating) { - - emit_push_reg_now ("edx"); - - emit_load_floating_rhs_expression_now (deref_size); - emit_pop_reg_now ("edx"); - - emit_store_floating_member_to_addr_reg_now ("edx", 0, deref_size); - expect_semi_or_recover (); - - free (name); - return 1; - - } - - 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) && !deref_is_floating) { - 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, src ? src->pointed_is_unsigned : get_global_symbol_pointed_is_unsigned (name)); - - 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 is_simple_assign; - 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; - int member_assignment_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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - 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 (is_simple_assign) { - - member_assignment_is_floating = member_is_floating || rhs_current_operand_is_floating_now (); - emit_push_reg_now ("edx"); - - if (member_assignment_is_floating) { - 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 (current_integer_expr_is_foldable_now ()) { - - 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_assignment_is_floating && is_simple_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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - 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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - get_token (); - - if (state->ofp) { - - if (is_simple_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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - 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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - get_token (); - - if (state->ofp) { - - if (is_simple_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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - 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 (is_simple_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) { - - enum token_kind member_calling_convention = last_found_member_calling_convention; - - 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 = scc_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 **) xrealloc (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) { - scc_close (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); - } - - scc_close (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"); - - if (member_calling_convention == TOK_STDCALL) { - fprintf (state->ofp, " add esp, %d\n", (DATA_PTR & 0x1f)); - } else { - 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"); - - if (member_calling_convention == TOK_STDCALL) { - fprintf (state->ofp, " addl $%d, %%esp\n", (DATA_PTR & 0x1f)); - } else { - 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; - - is_simple_assign = (tok.kind == TOK_ASSIGN); - 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 (is_simple_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 (is_simple_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 (is_simple_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 (is_simple_assign) { - emit_load_assignment_rhs_expression_to_reg ("eax"); - } else { - - if (lhs) { - - if (lhs->is_static && lhs->static_label) { - emit_load_global_to_reg_ex ("eax", lhs->static_label, lhs->size, lhs->is_unsigned); - } else { - emit_load_local_to_reg_ex ("eax", lhs->offset, lhs->size, lhs->is_unsigned); - } - - } 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_CCHAR || k == TOK_LCHAR || k == TOK_CINT || k == TOK_CUINT || k == TOK_CLONG || k == TOK_CULONG || k == TOK_CLLONG || k == TOK_CULLONG; -} - -static int token_is_integer_unsigned_constant_now (enum token_kind k) { - return k == TOK_CUINT || k == TOK_CULONG || k == TOK_CULLONG; -} - -#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 void emit_statement_const32_to_edx (int64_s v) { - - flush_pending_statement_labels (); - - if (!state->ofp) { - return; - } - - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " mov edx, %lu\n", v.low & U32_MASK); - } else { - fprintf (state->ofp, " movl $%lu, %%edx\n", v.low & U32_MASK); - } - -} - -static int emit_statement_rhs_const32_to_edx_if_possible (void) { - - int64_s v; - - if (token_is_const_condition_operand_now ()) { - - if (current_integer_expr_is_foldable_now ()) { - v = const64_from_current_foldable_expr (); - } else { - v = const64_from_current_operand (); - } - - emit_statement_const32_to_edx (v); - return 1; - - } - - if (tok.kind == TOK_IDENT && tok.ident && !find_local_symbol (tok.ident) && find_global_symbol (tok.ident) < 0 && resolve_enum_constant (tok.ident, &v)) { - - get_token (); - - emit_statement_const32_to_edx (v); - return 1; - - } - - return 0; - -} - -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; - } - - /* - * Parenthesized floating conditions such as: - * - * if (num < 0) - * while (b >= 10.0) - * - * arrive here while TOK_LPAREN is still current. If we don't recognise - * the name inside the parens as floating, the generic parenthesized - * integer path consumes the expression and compares only the low dword. - */ - if (find_local_symbol (name)) { - - struct local_symbol *src = find_local_symbol (name); - return src && src->is_floating ? 1 : 0; - - } - - if (find_global_symbol (name) >= 0) { - return get_global_symbol_floating (name) ? 1 : 0; - } - - return 0; - -} - -static int source_va_arg_type_is_double_now (const char *p) { - - int paren; - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p != '(') { - return 0; - } - - paren = 0; - - while (*p) { - - if (*p == '(') { - paren++; - } else if (*p == ')') { - - paren--; - - if (paren == 0) { - return 0; - } - - } else if (paren == 1 && *p == ',') { - - p++; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (strncmp (p, "double", 6) == 0 && - !(p[6] == '_' || (p[6] >= '0' && p[6] <= '9') || - (p[6] >= 'A' && p[6] <= 'Z') || - (p[6] >= 'a' && p[6] <= 'z'))) { - return 1; - } - - } - - p++; - - } - - return 0; - -} - -static int rhs_current_operand_is_floating_now (void) { - - if (token_is_floating_constant_now ()) { - return 1; - } - - if (tok.kind == TOK_SCC_BUILTIN_VA_ARG) { - - const char *p = tok.caret ? tok.caret + tok.len : 0; - - if (source_va_arg_type_is_double_now (p)) { - 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 (strcmp (tok.ident, "__scc_builtin_va_arg") == 0 && source_va_arg_type_is_double_now (p)) { - return 1; - } - - if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) { - return get_global_function_returns_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_function_returns_floating (tok.ident) ? 1 : 0; - } - - } - - return 0; - -} - -static int64_s floating_ld_to_bits_now (int size, 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, 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) { - - switch (k) { - - case TOK_LESS: - case TOK_LTEQ: - case TOK_GREATER: - case TOK_GTEQ: - case TOK_EQEQ: - case TOK_NOTEQ: - - return 1; - - default: - - return 0; - - } - -} - -static void emit_statement_cmp_eax_edx_jump_if_false (enum token_kind op, int is_unsigned, int label); -static int statement_condition_emit_logical_tail (int label); - -static void emit_statement_jump_if_false (int label); -static int emit_statement_cmp_eax_edx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label); - -static int source_condition_tail_end_now (const char *p) { - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - switch (*p) { - - case '\0': - case ')': - case ';': - case '{': - - return 1; - - default: - - break; - - } - - return 0; - -} - -static int source_condition_skip_rhs_const_or_enum_now (const char **pp) { - - char word[128]; - - const char *p; - int i; - - int64_s ignored; - - if (!pp || !*pp) { - return 0; - } - - p = *pp; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { - - i = 0; - - while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || - (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && - i + 1 < (int) sizeof (word)) { - i++; - } - - memcpy (word, p, (unsigned long) i); - word[i] = 0; - - if (!resolve_enum_constant (word, &ignored)) { - return 0; - } - - p += i; - - *pp = p; - return 1; - - } - - if (*p == '\'') { - - p++; - - while (*p && *p != '\'') { - - if (*p == '\\' && p[1]) { - p += 2; - } else { - p++; - } - - } - - if (*p != '\'') { - return 0; - } - - p++; - - *pp = p; - return 1; - - } - - if (*p == '-' || *p == '+') { - p++; - } - - if (*p < '0' || *p > '9') { - return 0; - } - - while ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'F') || - (*p >= 'a' && *p <= 'f') || - *p == 'x' || *p == 'X' || - *p == 'u' || *p == 'U' || - *p == 'l' || *p == 'L') { - p++; - } - - *pp = p; - return 1; - -} - -static int emit_statement_rhs_const32_or_enum_to_edx_if_possible (void) { - - int64_s v; - - if (tok.kind == TOK_IDENT && tok.ident && resolve_enum_constant (tok.ident, &v)) { - - get_token (); - - emit_statement_const32_to_edx (v); - return 1; - - } - - if (token_is_const_condition_operand_now ()) { - - if (current_integer_expr_is_foldable_now ()) { - v = const64_from_current_foldable_expr (); - } else { - v = const64_from_current_operand (); - } - - emit_statement_const32_to_edx (v); - return 1; - - } - - return 0; - -} - -static int source_condition_logical_rhs_is_enum_compare_now (const char *p) { - - if (!p) { - return 0; - } - - for (;;) { - - if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) { - p += 2; - } else { - return 0; - } - - 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 == '_') { - p++; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '(') { - return 0; - } - - for (;;) { - - if (*p == '.') { - p++; - } else if (p[0] == '-' && p[1] == '>') { - p += 2; - } else { - break; - } - - 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 == '_') { - p++; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - } - - if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { - p += 2; - } else if (*p == '<' || *p == '>') { - p++; - } else { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (!source_condition_skip_rhs_const_or_enum_now (&p)) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (source_condition_tail_end_now (p)) { - return 1; - } - - if (!((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|'))) { - return 0; - } - - } - -} - -static const char *source_find_current_ident_on_line (const char *p) { - - int name_len; - - if (tok.kind != TOK_IDENT || !tok.ident || !p) { - return 0; - } - - name_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) name_len) == 0 && - !ident_char_now ((unsigned char) p[name_len])) { - return p + name_len; - } - - p++; - - } - - return 0; - -} - -static int source_condition_ident_enum_compare_now (const char *p) { - - if (tok.kind != TOK_IDENT || !tok.ident || !p) { - return 0; - } - - /* - * tok.start is usually the beginning of the source line, not the current - * identifier. When compiling the compiler itself this made simple enum - * tests such as: - * - * if (assign_op == TOK_ASSIGN) - * - * miss the fixed compare-emission path and fall into the generic - * expression folder, where later bootstrap stages reduced the condition - * to "mov eax, TOK_ASSIGN; test eax, eax". Locate the current token on - * the line first, then inspect the text that follows it. - */ - p = source_find_current_ident_on_line (p); - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '(' || *p == '.' || (p[0] == '-' && p[1] == '>')) { - return 0; - } - - if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { - p += 2; - } else if (*p == '<' || *p == '>') { - p++; - } else { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (!source_condition_skip_rhs_const_or_enum_now (&p)) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - /* - * This fast path emits a branch-form compare, which is exactly what is - * needed for logical tails. Do not send enum-token compares through the - * generic expression path: later self-built stages have repeatedly reduced - * them to "mov eax, ; test eax,eax". - */ - if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { - return source_condition_logical_rhs_is_enum_compare_now (p); - } - - return source_condition_tail_end_now (p); - -} - -static int source_condition_ident_immediate_compare_now (const char *p) { - - int name_len; - - if (tok.kind != TOK_IDENT || !tok.ident || !p) { - return 0; - } - - name_len = (int) strlen (tok.ident); - p = source_find_current_ident_on_line (p); - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - /* - * Only catch immediate primary/member comparisons here. Do not claim - * calls, subscripts, or arithmetic expressions; those still need the - * ordinary expression parser. - */ - if (*p == '(' || *p == '[') { - return 0; - } - - for (;;) { - - if (*p == '.') { - p++; - } else if (p[0] == '-' && p[1] == '>') { - p += 2; - } else { - break; - } - - 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 == '_') { - p++; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - } - - if ((p[0] == '=' && p[1] == '=') || - (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || - (p[0] == '>' && p[1] == '=')) { - p += 2; - } else if ((p[0] == '<' && p[1] != '<') || - (p[0] == '>' && p[1] != '>')) { - p++; - } else { - - (void) name_len; - return 0; - - } - - /* - * This shortcut emits one cmp/jcc and returns to the statement parser. - * It must only claim a whole simple condition. If the comparison is - * followed by a top-level logical operator, for example: - * - * m->nargs >= 0 || m->is_variadic - * ch >= 'a' && ch <= 'f' - * - * then claiming only the first comparison leaves the ||/&& tail to a - * parser path that expects an integer constant expression. Let the - * ordinary expression parser own those full logical conditions instead. - */ - { - - int paren_depth = 0; - int bracket_depth = 0; - - while (*p) { - - if (*p == '\'' || *p == '"') { - - int quote = *p++; - - while (*p) { - - if (*p == '\\' && p[1]) { - - p += 2; - continue; - - } - - if (*p == quote) { - - p++; - break; - - } - - p++; - - } - - continue; - - } - - if (*p == '(') { - - paren_depth++; - p++; - - continue; - - } - - if (*p == ')') { - - if (paren_depth == 0) { - break; - } - - paren_depth--; - p++; - - continue; - - } - - if (*p == '[') { - - bracket_depth++; - p++; - - continue; - - } - - if (*p == ']') { - - if (bracket_depth > 0) { - bracket_depth--; - } - - p++; - continue; - - } - - if (paren_depth == 0 && bracket_depth == 0 && - ((p[0] == '&' && p[1] == '&') || - (p[0] == '|' && p[1] == '|'))) { - return 0; - } - - p++; - - } - - } - - (void) name_len; - return 1; - -} - -static int emit_statement_ident_immediate_compare_jump_if_false_now (int label) { - - enum token_kind op; - int is_unsigned; - - if (!source_condition_ident_immediate_compare_now (tok.caret) && - !source_condition_ident_immediate_compare_now (tok.start)) { - return 0; - } - - is_unsigned = rhs_current_operand_is_unsigned_now (); - emit_load_assignment_rhs_to_reg ("eax"); - - while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { - emit_apply_postfix_member_access_to_reg_now ("eax"); - } - - if (postfix_member_seen && postfix_member_is_unsigned) { - is_unsigned = 1; - } - - if (!token_is_statement_compare_operator (tok.kind)) { - return 0; - } - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return 1; - -} - -static int emit_statement_ident_enum_compare_jump_if_false_now (int label) { - - enum token_kind op; - int is_unsigned; - - if (!source_condition_ident_enum_compare_now (tok.caret) && !source_condition_ident_enum_compare_now (tok.start)) { - return 0; - } - - is_unsigned = rhs_current_operand_is_unsigned_now (); - emit_load_assignment_rhs_to_reg ("eax"); - - if (!token_is_statement_compare_operator (tok.kind)) { - return 0; - } - - op = tok.kind; - get_token (); - - /* - * This path is specifically an EAX-vs-constant statement comparison. - * Do not route the RHS through the generic "load constant to named - * register" helper here. During bootstrap the named-register helper path - * is fragile: if the next-stage compiler mis-parses the string argument or - * folds the call badly, enum compares such as: - * - * if (unary_op == TOK_MINUS) - * - * can degrade into: - * - * mov eax, TOK_MINUS - * test eax, eax - * - * which makes every non-zero enum condition true. Emit the RHS into EDX - * directly so the following cmp/jcc sequence is structurally fixed. - */ - if (!emit_statement_rhs_const32_or_enum_to_edx_if_possible ()) { - return 0; - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return 1; - -} - -static int source_condition_member_enum_compare_now (const char *p) { - - char word[128]; - int i; - - if (tok.kind != TOK_IDENT || !tok.ident || !p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (strncmp (p, tok.ident, (unsigned long) strlen (tok.ident)) == 0 && !ident_char_now ((unsigned char) p[strlen (tok.ident)])) { - p += strlen (tok.ident); - } else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') { - - while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') { - p++; - } - - } else if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { - return 0; - } - - for (;;) { - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '.') { - p++; - } else if (p[0] == '-' && p[1] == '>') { - p += 2; - } else { - return 0; - } - - 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 == '_') { - p++; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (!(*p == '.' || (p[0] == '-' && p[1] == '>'))) { - break; - } - - } - - if ((p[0] == '=' && p[1] == '=') || (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || (p[0] == '>' && p[1] == '=')) { - p += 2; - } else if (*p == '<' || *p == '>') { - p++; - } else { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (!source_condition_skip_rhs_const_or_enum_now (&p)) { - return 0; - } - - i = 0; - - while (((p[i] >= 'A' && p[i] <= 'Z') || (p[i] >= 'a' && p[i] <= 'z') || (p[i] >= '0' && p[i] <= '9') || p[i] == '_') && i + 1 < (int) sizeof (word)) { - i++; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|')) { - return source_condition_logical_rhs_is_enum_compare_now (p); - } - - return source_condition_tail_end_now (p); - -} - -static int emit_statement_member_enum_compare_jump_if_false_now (int label) { - - enum token_kind op; - int is_unsigned; - - if (!source_condition_member_enum_compare_now (tok.caret) && !source_condition_member_enum_compare_now (tok.start)) { - return 0; - } - - is_unsigned = rhs_current_operand_is_unsigned_now (); - emit_load_assignment_rhs_to_reg ("eax"); - - while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) { - emit_apply_postfix_member_access_to_reg_now ("eax"); - } - - if (postfix_member_seen && postfix_member_is_unsigned) { - is_unsigned = 1; - } - - if (!token_is_statement_compare_operator (tok.kind)) { - return 0; - } - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_or_enum_to_edx_if_possible ()) { - return 0; - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return 1; - -} - -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 (double left, enum token_kind op, 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.0; - - } - -} - -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 void emit_statement_test_pair_jump_if_true (const char *lo, const char *hi, int label) { - - if (!state->ofp) { - return; - } - - 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"), label); - fprintf (state->ofp, " test %s, %s\n", lo, lo); - 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", hi, hi); - fprintf (state->ofp, " jnz .L%d\n", label); - fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo); - fprintf (state->ofp, " jnz .L%d\n", label); - - } - -} - -static int statement_condition_constant_known = 0; -static int statement_condition_constant_value = 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_ex ("eax", lhs->static_label, lhs->size, lhs->is_unsigned); - } else { - emit_load_local_to_reg_ex ("eax", lhs->offset, lhs->size, lhs->is_unsigned); - } - - } 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 = scc_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); - scc_close (step_tmp); - - step_tmp = 0; - - } - - emit_statement_jump (loop_label); - - } - - if (step_tmp) { - scc_close (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 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 void emit_statement_eax_truth_jump_if_false_and_tail (int label) { - - int skip_label; - - if (tok.kind == TOK_LOGAND) { - - emit_statement_test_eax_jump_if_false (label); - - get_token (); - emit_statement_jump_if_false (label); - - return; - - } - - if (tok.kind == TOK_LOGOR) { - - skip_label = anon_label++; - emit_test_reg_jump_nonzero_now ("eax", skip_label); - get_token (); - emit_statement_jump_if_false (label); - emit_statement_label (skip_label); - return; - - } - - emit_statement_test_eax_jump_if_false (label); - -} - -static int emit_statement_cmp_eax_edx_jump_if_false_and_tail (enum token_kind op, int is_unsigned, int label) { - - int rhs_label; - int skip_label; - - if (tok.kind == TOK_LOGAND) { - - get_token (); - - emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); - emit_statement_jump_if_false (label); - - return 1; - - } - - if (tok.kind == TOK_LOGOR) { - - rhs_label = anon_label++; - skip_label = anon_label++; - - get_token (); - - emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, rhs_label); - emit_statement_jump (skip_label); - - emit_statement_label (rhs_label); - emit_statement_jump_if_false (label); - - emit_statement_label (skip_label); - return 1; - - } - - emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label); - return 1; - -} - -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; - } - - /* - * Only claim a single parenthesized assignment here. Do not treat - * a parenthesized logical group whose first term is an assignment as - * one big assignment expression. For example: - * - * ((op2 = get_op (pp)) || (brackets && **pp == ')')) - * - * The old scanner consumed both leading '(' characters, parsed only - * the first assignment, and then tried to manage the outer group's - * closing ')' with parenthesized_assignment_open_parens. That left - * the statement-condition parser out of sync at the final ')' before - * the following '{'. - * - * A real parenthesized-assignment fast path starts with exactly one - * '(' at the current token. Nested/grouped logical expressions must - * be left to the normal expression parser. - */ - parens = 1; - p++; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '(') { - return 0; - } - - 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++; - } - - 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 (current_integer_expr_is_foldable_now ()) { - - 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_ex (reg, lhs->static_label, lhs->size, lhs->is_unsigned); - } else { - emit_load_local_to_reg_ex (reg, lhs->offset, lhs->size, lhs->is_unsigned); - } - - } 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 int source_condition_has_top_level_compare_now (const char *p) { - - int paren_depth = 0; - int bracket_depth = 0; - int saw_operand = 0; - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - while (*p) { - - if (*p == '\'' || *p == '"') { - - int quote = *p++; - - while (*p) { - - if (*p == '\\' && p[1]) { - - p += 2; - continue; - - } - - if (*p == quote) { - - p++; - break; - - } - - p++; - - } - - saw_operand = 1; - continue; - - } - - if (*p == '(') { - - paren_depth++; - - saw_operand = 1; - p++; - - continue; - - } - - if (*p == ')') { - - if (paren_depth == 0) { - return 0; - } - - paren_depth--; - saw_operand = 1; - - p++; - continue; - - } - - if (*p == '[') { - - bracket_depth++; - - saw_operand = 1; - p++; - - continue; - - } - - if (*p == ']') { - - if (bracket_depth > 0) { - bracket_depth--; - } - - saw_operand = 1; - p++; - - continue; - - } - - if (paren_depth == 0 && bracket_depth == 0) { - - if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|') || - *p == '?' || *p == ':' || *p == ',' || *p == ';') { - return 0; - } - - if (saw_operand) { - - if ((p[0] == '=' && p[1] == '=') || - (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || - (p[0] == '>' && p[1] == '=')) { - return 1; - } - - if ((p[0] == '<' && p[1] != '<') || - (p[0] == '>' && p[1] != '>')) { - return 1; - } - - } - - } - - if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { - saw_operand = 1; - } - - p++; - - } - - return 0; - -} - -static int source_parenthesized_lhs_followed_by_compare_now (const char *p) { - - int paren_depth = 0; - - if (!p) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p != '(') { - return 0; - } - - while (*p) { - - if (*p == '\'' || *p == '"') { - - int quote = *p++; - - while (*p) { - - if (*p == '\\' && p[1]) { - - p += 2; - continue; - - } - - if (*p == quote) { - - p++; - break; - - } - - p++; - - } - - continue; - - } - - if (*p == '(') { - - paren_depth++; - p++; - - continue; - - } - - if (*p == ')') { - - paren_depth--; - p++; - - if (paren_depth == 0) { - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if ((p[0] == '=' && p[1] == '=') || - (p[0] == '!' && p[1] == '=') || - (p[0] == '<' && p[1] == '=') || - (p[0] == '>' && p[1] == '=')) { - return 1; - } - - if ((p[0] == '<' && p[1] != '<') || - (p[0] == '>' && p[1] != '>')) { - return 1; - } - - return 0; - - } - - if (paren_depth < 0) { - return 0; - } - - continue; - - } - - p++; - - } - - return 0; - -} - -static int emit_statement_parenthesized_lhs_compare_jump_if_false_now (int label) { - - enum token_kind op; - int is_unsigned; - - if (tok.kind != TOK_LPAREN) { - return 0; - } - - if (!source_parenthesized_lhs_followed_by_compare_now (tok.caret)) { - return 0; - } - - get_token (); - - is_unsigned = rhs_current_operand_is_unsigned_now (); - emit_load_assignment_rhs_expression_to_reg ("eax"); - - expect (TOK_RPAREN, ")"); - - if (!token_is_statement_compare_operator (tok.kind)) { - - emit_statement_eax_truth_jump_if_false_and_tail (label); - return 1; - - } - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return 1; - -} - -static int emit_statement_direct_compare_jump_if_false_now (int label) { - - enum token_kind op; - - int is_unsigned; - int old_assignment32_stop_before_condition_operator; - - if (!source_condition_has_top_level_compare_now (tok.caret) && - !source_condition_has_top_level_compare_now (tok.start)) { - return 0; - } - - /* - * Do not let the generic integer direct-compare fast path consume - * floating conditions such as: - * - * while (value_fractional_d - ((int64_t)value_fractional_d) != 0) - * - * That path lowers the LHS through eax and compares integer dwords, - * so the example above becomes low_word - low_word and the fractional - * loop exits immediately. Leave floating expressions for the x87 - * condition path below. - */ - if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) { - return 0; - } - - is_unsigned = rhs_current_operand_is_unsigned_now (); - - old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; - assignment32_stop_before_condition_operator = 1; - - if (emit_load_assignment_binary_expression_prec_to_reg ("eax", 1)) { - is_unsigned = 1; - } - - assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; - - if (!token_is_statement_compare_operator (tok.kind)) { - - emit_statement_eax_truth_jump_if_false_and_tail (label); - return 1; - - } - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return 1; - -} - -static void emit_statement_jump_if_false (int label) { - - enum token_kind op; - int is_unsigned; - - const char *call_start; - const char *call_caret; - - char *call_name; - unsigned long call_line; - - int old_assignment32_stop_before_condition_operator; - flush_pending_statement_labels (); - - statement_condition_constant_known = 0; - statement_condition_constant_value = 0; - - if (emit_statement_ident_immediate_compare_jump_if_false_now (label)) { - return; - } - - if (emit_statement_ident_enum_compare_jump_if_false_now (label)) { - return; - } - - if (emit_statement_member_enum_compare_jump_if_false_now (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_compare_expression_to_reg ("edx"); - consume_parenthesized_assignment_remaining_closes (); - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - statement_condition_emit_logical_tail (label); - - return; - - } - - consume_parenthesized_assignment_remaining_closes (); - - emit_statement_eax_truth_jump_if_false_and_tail (label); - return; - - } - - if (emit_statement_parenthesized_lhs_compare_jump_if_false_now (label)) { - return; - } - - if (emit_statement_direct_compare_jump_if_false_now (label)) { - return; - } - - if (tok.kind == TOK_LPAREN && (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ())) { - - double left_float = 0.0; - - 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 ())) { - - 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 = int64_to_double_now (right_int); - - } - - 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 (floating_rhs_result_in_eax_bool) { - - emit_statement_eax_truth_jump_if_false_and_tail (label); - - floating_rhs_result_in_eax_bool = 0; - return; - - } - - if (left_float_constant) { - - statement_condition_constant_known = 1; - statement_condition_constant_value = left_float != 0.0 ? 1 : 0; - - if (statement_condition_fold_logical_tail (label)) { - return; - } - - return; - - } - - emit_load_floating_ld_now (float_size, 0.0); - - emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); - return; - - } - - /* - * Parenthesized statement conditions must be parsed as ordinary - * expressions here. The older recursive/special-case paths try to split - * things like: - * - * (p - q) > 2 - * ((*u++ = *t++) != '\n') - * (toupper((unsigned char)specifier) == 'X') - * - * into a hand-emitted branch form. That interacts badly with the - * stop-before-condition-operator guard and makes the inner expression - * parser treat non-constant subexpressions as integer constant - * expressions. Let the normal expression parser consume the whole - * parenthesized expression, then branch on the resulting value. If the - * parenthesized value is followed by a comparison, handle that comparison - * normally after the grouped LHS has been loaded. - */ - if (tok.kind == TOK_LPAREN) { - - 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; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return; - - } - - emit_statement_eax_truth_jump_if_false_and_tail (label); - return; - - } - - if (tok.kind == TOK_LPAREN && !source_starts_with_parenthesized_assignment_now ()) { - - get_token (); - - emit_statement_jump_if_false (label); - expect (TOK_RPAREN, ")"); - - statement_condition_emit_logical_tail (label); - return; - } - - if (statement_condition_starts_with_ident_call_now ()) { - - is_unsigned = rhs_current_operand_is_unsigned_now (); - - if (tok.kind != TOK_IDENT) { - emit_load_assignment_rhs_expression_to_reg ("eax"); - } else { - - call_name = xstrdup (tok.ident); - call_start = tok.start; - call_caret = tok.caret; - call_line = get_line_number (); - - get_token (); - - 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, "eax", call_start, call_caret, call_line); - free (call_name); - - } - - if (token_is_statement_compare_operator (tok.kind)) { - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return; - - } - - emit_statement_eax_truth_jump_if_false_and_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 ()) { - - double left_float = 0.0; - - 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 ())) { - - 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 = int64_to_double_now (right_int); - - } - - 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 (floating_rhs_result_in_eax_bool) { - - emit_statement_eax_truth_jump_if_false_and_tail (label); - - floating_rhs_result_in_eax_bool = 0; - return; - - } - - if (left_float_constant) { - - statement_condition_constant_known = 1; - statement_condition_constant_value = left_float != 0.0 ? 1 : 0; - - if (statement_condition_fold_logical_tail (label)) { - return; - } - - return; - - } - - emit_load_floating_ld_now (float_size, 0.0); - emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label); - - return; - - } - - /* - * Only take the statement-condition constant shortcut when the current - * token itself is a constant operand. The old text-based fallbacks could - * misfire during self-compilation of enum comparisons such as: - * - * if (assign_op == TOK_ASSIGN) - * - * and reduce the whole condition to the RHS enum value. That generated - * "mov eax, 61; test eax, eax" and made every compound assignment look - * like a plain assignment in the next bootstrap stage. - */ - if (token_is_const_condition_operand_now ()) { - - int fold_whole_expr = current_integer_expr_is_foldable_now (); - 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_statement_cmp_eax_edx_jump_if_false_and_tail (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 ()) { - - double left_float; - double right_float; - - left_float = int64_to_double_now (left); - 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 (current_integer_expr_is_foldable_now ()) { - 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 ()) { - - double left_float = int64_to_double_now (left); - - 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_and_tail (op, is_unsigned, 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_eax_truth_jump_if_false_and_tail (label); - return; - - } - - if (current_expression_mentions_64bit_symbol_now ()) { - - int old_assignment64_stop_before_condition_operator; - is_unsigned = rhs_current_operand_is_unsigned_now (); - - old_assignment64_stop_before_condition_operator = assignment64_stop_before_condition_operator; - assignment64_stop_before_condition_operator = 1; - - emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned); - assignment64_stop_before_condition_operator = old_assignment64_stop_before_condition_operator; - - 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"); - - } - - } - - /* - * The stop-before-compare guard is only needed while loading the - * left hand side of the statement condition. After the condition - * operator has been consumed, the right hand side must be parsed as - * a complete expression. Keeping the guard enabled here makes a - * parenthesized conditional expression such as: - * - * i < (len_fract == 0 ? 1 : precision - len_fract) - * - * stop at the nested == and leave the parser expecting the closing - * ')'. That is what produced the false "expected )" diagnostic. - */ - 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; - - } - - if (tok.kind == TOK_LOGAND) { - - get_token (); - - emit_statement_test_pair_jump_if_false ("eax", "edx", label); - emit_statement_jump_if_false (label); - - return; - - } - - if (tok.kind == TOK_LOGOR) { - - int skip_label = anon_label++; - get_token (); - - emit_statement_test_pair_jump_if_true ("eax", "edx", skip_label); - emit_statement_jump_if_false (label); - emit_statement_label (skip_label); - - return; - - } - - emit_statement_test_pair_jump_if_false ("eax", "edx", label); - return; - - } - - is_unsigned = rhs_current_operand_is_unsigned_now (); - - old_assignment32_stop_before_condition_operator = assignment32_stop_before_condition_operator; - assignment32_stop_before_condition_operator = 1; - - emit_load_assignment_rhs_expression_to_reg ("eax"); - assignment32_stop_before_condition_operator = old_assignment32_stop_before_condition_operator; - - if (token_is_statement_compare_operator (tok.kind)) { - - op = tok.kind; - get_token (); - - if (rhs_current_operand_is_unsigned_now ()) { - is_unsigned = 1; - } - - if (!emit_statement_rhs_const32_to_edx_if_possible ()) { - - emit_push_reg_now ("eax"); - emit_load_assignment_compare_expression_to_reg ("edx"); - emit_pop_reg_now ("eax"); - - } - - emit_statement_cmp_eax_edx_jump_if_false_and_tail (op, is_unsigned, label); - return; - - } - - emit_statement_eax_truth_jump_if_false_and_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 (postfix_member_is_floating || token_is_floating_constant_now () || rhs_current_operand_is_floating_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 = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_tag_name (name) : 0; - - } - - } else if (tok.kind == TOK_DOT) { - - if (src) { - - base_size = src->size; - base_tag_name = src->tag_name; - - } else { - - base_size = get_global_symbol_size (name); - base_tag_name = get_global_symbol_tag_name (name); - - } - - } 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) { - - int target_label = current_break_label; - - long cleanup_base = current_break_cleanup_base; - long cleanup_bytes; - - get_token (); - - if (target_label >= 0) { - - cleanup_bytes = current_block_cleanup_bytes - cleanup_base; - - if (!current_function_uses_single_frame && 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_CONTINUE) { - - int target_label = current_continue_label; - - long cleanup_base = current_continue_cleanup_base; - long cleanup_bytes; - - get_token (); - - if (target_label >= 0) { - - cleanup_bytes = current_block_cleanup_bytes - cleanup_base; - - if (!current_function_uses_single_frame && 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 *function_tmp = 0; - - char *inline_asm_text = 0; - char *function_asm_text = 0; - - int capture_inline_body = 0; - int capture_function_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; - capture_function_body = saved_ofp != 0; - - if (capture_function_body) { - - function_tmp = scc_tmpfile (); - - if (!function_tmp) { - capture_function_body = 0; - } - - } - - if (emit_body) { - - if (parsed_dllimport) { - report_line_at (get_filename (), last_declarator_name_line, REPORT_ERROR, last_declarator_name_start, last_declarator_name_caret, "function '%s' cannot be defined with '__dllimport'", name); - } - - 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_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); - set_global_symbol_dllimport (name, 0); - 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_calling_convention = (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention; - current_function_param_stack_bytes = 0; - current_function_has_return_statement = 0; - - parse_old_style_param_decls (); - remove_duplicate_pending_params (); - - /* - * Old-style definitions start with an identifier list, so the first - * function-symbol install only knows the default int-sized parameter - * placeholders. The declaration list between ')' and '{' is what gives - * those parameters their real types. Refresh the saved call signature - * after that list has been parsed, otherwise later calls in the same - * translation unit pass 64-bit parameters as single words. - */ - if (emit_body && should_emit) { - copy_pending_params_to_global_symbol (name); - } - - current_function_param_stack_bytes = global_symbol_stdcall_stack_bytes (name); - - 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_function_body) { - state->ofp = function_tmp; - } - - current_return_label = anon_label++; - pending_return_jump = 0; - current_function_preserve_assignment64_regs = 0; - - /* - * Do not apply the whole-function frame deferral to inline-function - * capture. The inline expander analyses the captured body as it was - * emitted, including any stack movement before parameter references. - * Moving all automatic storage to a synthetic prologue changes that - * analysis and can corrupt the self-built compiler. - */ - current_function_frame_enabled = (!is_inline && capture_function_body); - emit_function_start (name, !emit_public); - - 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 (); - - current_function_frame_label = -1; - current_function_frame_deferred = 0; - current_function_frame_enabled = 0; - - emit_goto_trampolines (); - current_function_uses_single_frame = 0; - - if (capture_function_body && function_tmp) { - - function_asm_text = read_tmp_file_text (function_tmp); - - if (function_asm_text) { - - inline_asm_text = replace_function_frame_placeholder (function_asm_text, current_function_frame_size); - - if (!inline_asm_text) { - - inline_asm_text = function_asm_text; - function_asm_text = 0; - - } - - if (!capture_inline_body || emit_inline_definition_to_output) { - fputs (inline_asm_text, saved_ofp); - } - - if (capture_inline_body) { - - 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); - - } - - } - - if (function_asm_text) { - free (function_asm_text); - } - - if (inline_asm_text) { - - free (inline_asm_text); - inline_asm_text = 0; - - } - - scc_close (function_tmp); - function_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; - - } - - clear_pending_params (); - - declarator_function_param_count = 0; - declarator_function_has_prototype = 0; - declarator_function_is_variadic = 0; - - if (tok.kind != TOK_RPAREN) { - - int old_style_param_start = pending_param_count; - - for (;;) { - - if (tok.kind == TOK_ELLIPSIS) { - - declarator_function_is_variadic = 1; - declarator_function_has_prototype = 1; - - get_token (); - - } else if (is_type_start (tok.kind)) { - - int param_base_size; - int param_size; - int param_pointer_depth; - int saved_function_param_count; - - int count_this_param = 0; - int saw_void_param_list = 0; - - char *param_name = 0; - - parse_type_spec (); - param_base_size = parsed_type_size; - - preserve_pending_params++; - - 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 (); - } - - saved_function_param_count = declarator_function_param_count; - - if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) { - parse_declarator (¶m_name); - } - - preserve_pending_params--; - - 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); - param_pointer_depth = declarator_pointer_depth; - - if ((declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) && param_pointer_depth < 1) { - param_pointer_depth = 1; - } - - if (param_name && find_pending_param_from (param_name, old_style_param_start) >= 0) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", param_name); - } else { - - add_pending_param (param_name, param_size, type_alignment (param_size), - (declarator_is_pointer || declarator_has_array || declarator_has_function || 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), - param_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f), parsed_type_is_unsigned); - - } - - } else { - saw_void_param_list = 1; - } - - if (param_name) { - free (param_name); - } - - declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0); - declarator_function_has_prototype = declarator_function_has_prototype || count_this_param || saw_void_param_list; - - } else if (token_is_ident ()) { - - if (find_pending_param_from (tok.ident, old_style_param_start) >= 0) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", tok.ident); - } else { - - add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0, 0); - declarator_function_param_count++; - - } - - 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); - - if (parsed_dllexport || declarator_dllexport) { - - vec_push (&vec_dllexports, xstrdup (name)); - - declarator_dllexport = 0; - parsed_dllexport = 0; - - } - - 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_value (int64_s *values, int max_values, int *count, unsigned int value, int elem_size) { - - unsigned int mask = 0xffU; - - if (elem_size == (DATA_SHORT & 0x1f)) { - mask = 0xffffU; - } - - if (*count < max_values) { - zext64 (&values[*count], value & mask); - } - - (*count)++; - -} - -static int parsed_string_initializer_elem_size = DATA_CHAR & 0x1f; - -static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) { - append_global_init_value (values, max_values, count, value, DATA_CHAR & 0x1f); -} - -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) { - - int elem_size = DATA_CHAR & 0x1f; +void parse_string_initializer_values (int64_s *values, int max_values, int *count) { + + int elem_size = DATA_CHAR & 0x1f; while (is_string_token ()) { @@ -37529,1533 +1384,1916 @@ static void parse_string_initializer_values (int64_s *values, int max_values, in default: - ch = (unsigned char) *s; - - if (*s) { - s++; - } - + ch = (unsigned char) *s; + + if (*s) { + s++; + } + + break; + + } + + } else { + ch = (unsigned char) *s++; + } + + append_global_init_value (values, max_values, count, ch, elem_size); + + } + + get_token (); + + } + + append_global_init_value (values, max_values, count, 0, elem_size); + parsed_string_initializer_elem_size = elem_size; + +} + +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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_array_count; + + int size = DATA_INT & 0x1f, 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_first_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 (); - } else { - ch = (unsigned char) *s++; } - append_global_init_value (values, max_values, count, ch, elem_size); + expect (close, (close == TOK_RBRACK) ? "]" : ")"); } - - get_token (); } - append_global_init_value (values, max_values, count, 0, elem_size); - parsed_string_initializer_elem_size = elem_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++) { + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + if (size < 1) { + size = DATA_INT & 0x1f; + } + + return size; } -static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width) { +int parse_constexpr_address_of_null_member (int64_s *out) { - if (tok.kind == TOK_LBRACE) { + if (tok.kind != TOK_AMPER) { + return 0; + } - get_token (); - - while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + 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; +} + +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; + +} + +int is_type_start (enum token_kind k) { + + switch (k) { + + case TOK_IDENT: - parse_char_array_initializer_values (values, max_values, count, row_width); + if (token_is_ms_int_type_name ()) { + return 1; + } - if (!_accept (TOK_COMMA)) { - break; + if (is_current_typedef_name ()) { + return 1; } + + return 0; - } + case TOK_AUTO: case TOK_REGISTER: case TOK_STATIC: + case TOK_DLLEXPORT: case TOK_DLLIMPORT: + 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: - expect (TOK_RBRACE, "}"); - return; + return 1; + + default: + + break; + + } + + return 0; + +} + +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; + int saved_declarator_array_dimensions = declarator_array_dimensions; + + long saved_declarator_array_count = declarator_array_count; + long saved_declarator_first_array_count = declarator_first_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_string_token ()) { + if (is_type_start (tok.kind)) { - int64_s tmp[MAX_STRING_INIT_BYTES]; - int tmp_count = 0; + declarator_is_pointer = 0; + declarator_pointer_depth = 0; + declarator_has_array = 0; + declarator_has_function = 0; + declarator_array_unsized = 0; + declarator_array_count = 0; - int i; - int n; + parse_type_spec (); - parse_string_initializer_values (tmp, MAX_STRING_INIT_BYTES, &tmp_count); + size = parsed_type_size & 0x1f; + is_unsigned = parsed_type_is_unsigned; - if (row_width <= 0) { - n = tmp_count; - } else { - n = (tmp_count < row_width) ? tmp_count : (int) row_width; + if (tok.kind != TOK_RPAREN) { + parse_declarator (&name); } - for (i = 0; i < n; i++) { - append_global_init_byte (values, max_values, count, (unsigned int) (tmp[i].low & 0xff)); + if (declarator_is_pointer) { + + size = DATA_PTR; + + is_unsigned = 1; + is_pointer = 1; + } - while (row_width > 0 && i < row_width) { + if (tok.kind == TOK_RPAREN) { - append_global_init_byte (values, max_values, count, 0); - i++; + get_token (); + ok = 1; + } else { + expect (TOK_RPAREN, ")"); } - return; + if (name) { + free (name); + } } - if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) { + last_cast_type_tag_name[0] = '\0'; + last_cast_type_object_size = parsed_type_size & 0x1f; + last_cast_type_is_floating = (is_pointer ? 0 : parsed_type_is_floating); - if (*count < max_values) { - values[*count] = const64_from_current_expr (); - } else { - const64_from_current_expr (); - } - - (*count)++; + 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; + declarator_first_array_count = saved_declarator_first_array_count; + declarator_array_dimensions = saved_declarator_array_dimensions; + + 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; + +} + +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 int global_initializer_parenthesized_string_now (void) { +struct local_symbol local_symbols[MAX_LOCAL_SYMBOLS]; +int local_symbol_count = 0; + +struct local_symbol *find_local_symbol (const char *name) { - const char *p = tok.caret; + int i; - if (!p || *p != '(') { + if (!name) { return 0; } - p++; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } - - if (*p == '"') { - return 1; - } + for (i = local_symbol_count - 1; i >= 0; i--) { - if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') { - return 1; - } + if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) { + return &local_symbols[i]; + } - if (*p == 'u' && p[1] == '8' && p[2] == '"') { - return 1; } return 0; } -static int global_initializer_cast_before_string_now (void) { +struct global_symbol_entry global_symbols[MAX_GLOBAL_SYMBOLS]; +int global_symbol_count = 0; - const char *p = tok.caret; - int depth; - - if (!p || *p != '(') { - return 0; - } - - p++; - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } +int find_global_symbol (const char *name) { + + int i; - if (!(ident_start_now ((unsigned char) *p))) { - return 0; + if (!name) { + return -1; } - depth = 1; - - while (*p && depth > 0) { + for (i = 0; i < global_symbol_count; i++) { - if (*p == '(') { - depth++; - } else if (*p == ')') { - depth--; + if (strcmp (global_symbols[i].name, name) == 0) { + return i; } - - p++; } - if (depth != 0) { - return 0; - } - - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { - p++; - } + return -1; + +} + +int get_global_symbol_size (const char *name) { + + int i = find_global_symbol (name); - if (*p == '"') { - return 1; + if (i >= 0 && global_symbols[i].size > 0) { + return global_symbols[i].size; } - if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') { - return 1; - } + return DATA_INT & 0x1f; + +} + +int get_global_symbol_param_pointer_depth (const char *name, int param_index) { + + int i = find_global_symbol (name); - if (*p == 'u' && p[1] == '8' && p[2] == '"') { - return 1; + if (i >= 0 && param_index >= 0 && param_index < 128) { + return global_symbols[i].param_pointer_depths[param_index]; } return 0; } -static void append_global_zero_initializer_value (int64_s *values, char **symbols, int max_values, int *count) { +int get_global_symbol_pointer_depth (const char *name) { - if (*count < max_values) { - - values[*count].low = 0; - values[*count].high = 0; - - if (symbols) { - symbols[*count] = 0; - } + int i = find_global_symbol (name); + if (i >= 0) { + return global_symbols[i].pointer_depth; } - - (*count)++; + + return 0; } -static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count) { +int get_global_symbol_pointed_size (const char *name) { - int count = 0; - int i; - - for (i = 0; i < field_count; i++) { - - if (field_sizes[i] > 0) { - count++; - } + int i = find_global_symbol (name); + if (i >= 0 && global_symbols[i].pointed_size > 0) { + return global_symbols[i].pointed_size; } - return count; + return DATA_INT & 0x1f; } -static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count) { +int get_global_symbol_array (const char *name) { - if (element_field_count <= 0 || tok.kind != TOK_LBRACE) { - - parse_global_initializer_values (values, symbols, max_values, count); - return; + int i = find_global_symbol (name); + if (i >= 0) { + return global_symbols[i].is_array; } - get_token (); + return 0; + +} + +int get_global_symbol_array_element_size (const char *name) { + + int i = find_global_symbol (name); - while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { + if (i >= 0) { - int before = *count; - parse_global_initializer_values (values, symbols, max_values, count); + if (global_symbols[i].array_element_size > 0) { + return global_symbols[i].array_element_size; + } - if (*count > before) { + if (global_symbols[i].tag_name && global_symbols[i].tag_name[0]) { - while ((*count - before) < element_field_count) { - append_global_zero_initializer_value (values, symbols, max_values, count); + struct aggregate_tag_entry *entry = find_aggregate_tag (global_symbols[i].tag_name, 0); + + if (entry && entry->size > 0) { + return entry->size; } } - - if (!_accept (TOK_COMMA)) { - break; - } } - expect (TOK_RBRACE, "}"); + return 0; } -static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count) { +int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) { - if (tok.kind == TOK_LBRACE) { + int best; + int i; - 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; + last_found_member_tag_name = 0; + last_found_member_is_unsigned = 0; + last_found_member_calling_convention = TOK_EOF; + if (!name) { + return 0; } - if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) { + best = -1; - 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) { + for (i = member_info_count - 1; i >= 0; i--) { + + if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) { - values[*count] = const64_from_current_expr (); - - if (symbols) { - symbols[*count] = 0; - } - - (*count)++; + best = i; + break; - } else { - const64_from_current_expr (); } } -} - -static int int64_is_zero_value (int64_s value) { - return value.low == 0 && value.high == 0; -} - -static char masm_pending_data_label[512]; -static char masm_open_data_directive[8]; - -static int masm_has_pending_data_label = 0; -static int masm_data_line_open = 0; -static int masm_data_line_values = 0; - -static void masm_set_pending_data_label (const char *name) { - - const char *asm_name = asm_global_symbol_name (name); - unsigned long len; + if (best < 0) { + return 0; + } - if (!asm_name) { + last_found_member_tag_name = member_infos[best].tag_name; + last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0; + last_found_member_calling_convention = member_infos[best].calling_convention; + + if (offset) { + *offset = member_infos[best].offset; + } - masm_has_pending_data_label = 0; - masm_pending_data_label[0] = 0; - - return; + if (size) { + *size = member_infos[best].size; + } + if (elem_size) { + *elem_size = member_infos[best].elem_size; } - len = strlen (asm_name); + if (pointer_depth) { + *pointer_depth = member_infos[best].pointer_depth; + } - if (len >= sizeof (masm_pending_data_label)) { - len = sizeof (masm_pending_data_label) - 1; + if (is_array) { + *is_array = member_infos[best].is_array; } - memcpy (masm_pending_data_label, asm_name, len); + if (is_floating) { + *is_floating = member_infos[best].is_floating; + } - masm_pending_data_label[len] = 0; - masm_has_pending_data_label = 1; + return 1; } -static void masm_flush_data_line (void) { - - if (masm_data_line_open) { - - fprintf (state->ofp, "\n"); - - masm_data_line_open = 0; - masm_data_line_values = 0; - - masm_open_data_directive[0] = 0; - - } - -} +struct aggregate_tag_entry aggregate_tags[MAX_AGG_TAGS]; +int aggregate_tag_count = 0; -static void masm_emit_data_prefix (const char *directive) { +struct aggregate_tag_entry *find_aggregate_tag (const char *name, int is_union) { - if (masm_has_pending_data_label) { + int i; - masm_flush_data_line (); - fprintf (state->ofp, "%s %s ", masm_pending_data_label, directive); - - masm_has_pending_data_label = 0; - masm_pending_data_label[0] = 0; - - strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); - - masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; - masm_data_line_open = 1; - masm_data_line_values = 0; + if (!name) { + return 0; + } - } else if (masm_data_line_open && strcmp (masm_open_data_directive, directive) == 0) { + for (i = 0; i < aggregate_tag_count; i++) { - if (masm_data_line_values >= 8) { - - fprintf (state->ofp, "\n %s ", directive); - masm_data_line_values = 0; - - } else { - fprintf (state->ofp, ", "); + if (aggregate_tags[i].is_union == is_union && strcmp (aggregate_tags[i].name, name) == 0) { + return &aggregate_tags[i]; } - } else { - - masm_flush_data_line (); - - fprintf (state->ofp, " %s ", directive); - strncpy (masm_open_data_directive, directive, sizeof (masm_open_data_directive) - 1); - - masm_open_data_directive[sizeof (masm_open_data_directive) - 1] = 0; - masm_data_line_open = 1; - masm_data_line_values = 0; - } - masm_data_line_values++; + return 0; } -static void emit_global_scalar (int64_s value, int size) { +static void load_typedef_name (struct typedef_entry *entry) { - unsigned long high = value.high; - unsigned long low = value.low; + int i; + clear_parsed_fields (); - if (state->syntax & ASM_SYNTAX_MASM) { + if (!entry) { - if (size == (DATA_CHAR & 0x1f)) { - - masm_emit_data_prefix ("db"); - fprintf (state->ofp, "%ld", low & 0xFFUL); - - } else if (size == (DATA_SHORT & 0x1f)) { - - masm_emit_data_prefix ("dw"); - fprintf (state->ofp, "%ld", low & 0xFFFFUL); - - } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) { - - masm_emit_data_prefix ("dq"); - fprintf (state->ofp, "0%08lX%08lXh", high & U32_MASK, low & U32_MASK); - - } else { + parsed_type_size = DATA_INT & 0x1f; - masm_emit_data_prefix ("dd"); - fprintf (state->ofp, "%lu", low & U32_MASK); + 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; + parsed_calling_convention = TOK_EOF; - } + append_parsed_field (DATA_INT & 0x1f); + return; + + } - } else if (state->syntax & ASM_SYNTAX_NASM) { + parsed_type_size = entry->size; - 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); - } + 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_calling_convention = entry->calling_convention; - } else { + parsed_type_has_tag = 0; + parsed_type_tag_name[0] = '\0'; - 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)) { + if (entry->tag_name && entry->tag_name[0]) { - fprintf (state->ofp, " .long %lu\n", low & U32_MASK); - fprintf (state->ofp, " .long %lu\n", high & U32_MASK); + 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'; - } else { - fprintf (state->ofp, " .long %lu\n", low & U32_MASK); - } + parsed_type_has_tag = 1; + + } + for (i = 0; i < entry->field_count; i++) { + append_parsed_field (entry->field_sizes[i]); } } -static void emit_global_address (const char *symbol, int size) { +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) { - const char *asm_symbol; - int64_s zero; - - if (!symbol || !*symbol) { - - zero.low = 0; - zero.high = 0; - - emit_global_scalar (zero, size); + if (!name) { 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"); + if (member_info_count >= MAX_MEMBER_INFOS) { + return; } - emit_extern_reference_symbol (symbol, DATA_PTR); - asm_symbol = asm_global_symbol_name (symbol); - - if (state->syntax & ASM_SYNTAX_MASM) { + 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].calling_convention = (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention; + 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; - masm_emit_data_prefix ("dd"); - fprintf (state->ofp, "offset %s", asm_symbol); + member_info_count++; + +} + +static int fields_storage_size (const int *sizes, int count) { + + int total = 0, i; - } else if (state->syntax & ASM_SYNTAX_NASM) { - fprintf (state->ofp, " dd %s\n", asm_symbol); - } else { + for (i = 0; i < count; i++) { - if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, " .long offset %s\n", asm_symbol); + if (sizes[i] < 0) { + total += -sizes[i]; } else { - fprintf (state->ofp, " .long %s\n", asm_symbol); + total += sizes[i]; } } + + return total; } -static void emit_global_value (const int64_s *value, const char *symbol, int size) { +void add_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size, int pointed_is_unsigned) { - int64_s zero; - - if (symbol) { - - emit_global_address (symbol, size); - return; - + if (pointer_depth > 0 && parsed_type_is_aggregate && parsed_type_size > (DATA_PTR & 0x1f) && pointed_size <= (DATA_PTR & 0x1f)) { + pointed_size = parsed_type_size; } - if (value) { + if (pending_param_count >= MAX_PENDING_PARAMS) { - emit_global_scalar (*value, size); + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many function parameters"); return; } - zero.low = 0; - zero.high = 0; - - emit_global_scalar (zero, size); - -} - -static void emit_global_space (long bytes) { - - if (bytes <= 0) { - return; + if (size < 1) { + size = DATA_INT & 0x1f; } - if (state->syntax & ASM_SYNTAX_MASM) { - - masm_emit_data_prefix ("db"); - fprintf (state->ofp, "%ld dup (?)", 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; + if (align < 1) { + align = type_alignment (size); } - return field_size; + pending_params[pending_param_count].name = (name && *name) ? xstrdup (name) : 0; + 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].knr_declared = 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_is_floating = pointer_depth > 0 ? (parsed_type_is_floating ? 1 : 0) : 0; + pending_params[pending_param_count].pointed_is_unsigned = pointer_depth > 0 ? (pointed_is_unsigned ? 1 : 0) : 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 int global_initializer_is_all_zero (const int64_s *values, char **symbols, int value_count) { +int find_pending_param_from (const char *name, int start) { int i; - - if (symbols) { - for (i = 0; i < value_count; i++) { - if (symbols[i]) { - return 0; - } - } + + if (!name) { + return -1; } - - if (!values || value_count <= 0) { - return 1; + + if (start < 0) { + start = 0; } - - for (i = 0; i < value_count; i++) { - if (!int64_is_zero_value (values[i])) { - return 0; + + for (i = start; i < pending_param_count; i++) { + + if (pending_params[i].name && strcmp (pending_params[i].name, name) == 0) { + return i; } + } - - return 1; + + return -1; } -static void emit_global_label (const char *name, int is_static) { +char *take_ident (void) { - const char *asm_name = asm_global_symbol_name (name); - - if (state->syntax & ASM_SYNTAX_MASM) { + char *name; - if (!is_static) { - fprintf (state->ofp, "public %s\n", asm_name); - } - - fprintf (state->ofp, "%s:\n", asm_name); + if (!token_is_ident ()) { + return 0; + } - } else if (state->syntax & ASM_SYNTAX_NASM) { + name = xstrdup (tok.ident); - if (!is_static) { - fprintf (state->ofp, "global %s\n", asm_name); - } - - fprintf (state->ofp, "%s:\n", asm_name); + last_declarator_name_line = get_line_number (); + last_declarator_name_start = tok.start; + last_declarator_name_caret = tok.caret; - } else { + if (capture_declarator_name_location && !captured_declarator_name_location) { - if (!is_static) { - fprintf (state->ofp, ".globl %s\n", asm_name); - } - - fprintf (state->ofp, "%s:\n", asm_name); + 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 emit_global_data_label (const char *name, int is_static) { +long align_up_long (long value, int align) { - const char *asm_name = asm_global_symbol_name (name); + long mask; - if (state->syntax & ASM_SYNTAX_MASM) { + if (align <= 1) { + return value; + } - masm_flush_data_line (); - - if (!is_static) { - fprintf (state->ofp, "public %s\n", asm_name); - } - - masm_set_pending_data_label (name); + mask = (long) align - 1; + return (value + mask) & ~mask; + +} + +int type_alignment (int size) { + + if (size <= 1) { + return 1; + } - } else { - emit_global_label (name, is_static); + if (size == 2) { + return 2; } + + return 4; } -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) { +void parse_type_spec (void) { - int64_s zero; + int saw = 0; - int value_index, use_bss, i; - long emitted, total; - - zero.low = 0; - zero.high = 0; + int saw_float = 0; + int saw_double = 0; + int saw_void = 0; - if (!state->ofp || !name || !*name) { - return; - } + 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; - if (size <= 0) { - size = DATA_INT & 0x1f; - } + parsed_calling_convention = TOK_EOF; - if (array_count <= 0) { - array_count = 1; + if (declarator_depth == 0) { + declarator_calling_convention = TOK_EOF; } - total = size * array_count; - use_bss = global_initializer_is_all_zero (values, symbols, value_count); - - if (use_bss) { + parsed_dllexport = 0; + parsed_dllimport = 0; - switch_section (SECTION_BSS); + 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 || + tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT || tok.kind == TOK_STDCALL || token_is_ms_int_type_name ()) { - emit_global_data_label (name, is_static); - emit_global_space (total); + saw = 1; - return; - - } - - switch_section (SECTION_DATA); - emit_global_data_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); + 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; } - return; - - } - - if (is_aggregate && field_count > 0) { - - while (emitted < total) { + if (tok.kind == TOK_STDCALL || tok.kind == TOK_DLLEXPORT || tok.kind == TOK_DLLIMPORT) { - 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); - + parse_decl_modifier (); + continue; + + } else 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) { - emit_global_space (total - emitted); - return; - - } - - if (is_array) { - - int elem_size = size; + 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; + } - if (field_count > 0 && field_sizes[0] > 0) { - elem_size = field_sizes[0]; - } + } 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; + } - while (emitted + elem_size <= total) { + } else if (tok.kind == TOK_INLINE) { + parsed_type_is_inline = 1; + } else if (tok.kind == TOK_CHAR) { - if (value_index < value_count) { - - emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, elem_size); - value_index++; - + 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 { - emit_global_scalar (zero, elem_size); + parsed_type_size = DATA_CHAR; } - - emitted += elem_size; - } + } else if (tok.kind == TOK_SHORT) { - 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]; - char skip_label[64]; - - int elem_size = DATA_CHAR & 0x1f; - int value_count = 0, i; - - if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) { - sprintf (label, "LC%d", anon_label++); - } else { - sprintf (label, ".LC%d", anon_label++); - } - - parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count); - elem_size = parsed_string_initializer_elem_size; - - /* - * 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) { - - 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++); - } + 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; + } - fprintf (state->ofp, " jmp %s\n", skip_label); + } else if (tok.kind == TOK_INT) { - switch_section (SECTION_DATA); - emit_global_data_label (label, 1); + 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; + } - for (i = 0; i < value_count; i++) { - emit_global_scalar (values[i], elem_size); - } + } else if (tok.kind == TOK_LONG) { - switch_section (SECTION_TEXT); - emit_global_label (skip_label, 1); + 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; + } - return xstrdup (label); - - } - - switch_section (SECTION_DATA); - emit_global_data_label (label, 1); - - for (i = 0; i < value_count; i++) { - emit_global_scalar (values[i], elem_size); - } - - 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 = get_global_symbol_dllimport (name) ? asm_global_import_symbol_name (name) : asm_global_symbol_name (name); - - if (get_global_symbol_dllimport (name)) { - - size = DATA_PTR & 0x1f; - is_function = 0; + } 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_LONG) { + + parsed_type_size = DATA_DOUBLE; + saw_double = 1; + + } else 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 (); } - 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; + /* + * 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 ()) { - (void) size; - (void) is_function; + get_token (); + + saw = 1; + saw_real_type = 1; + + parsed_type_size = DATA_INT; - 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; - } + if (parsed_type_size == DATA_NONE) { - global_symbols[i].extern_emitted = 1; - -} - -static void emit_pending_extern_symbols (void) { - - int i; + parsed_type_only_qualifiers = (saw && !saw_real_type) ? 1 : 0; + parsed_type_size = DATA_INT; - if (!state->ofp) { - return; + } else if (parsed_type_size == DATA_LONG && state->long64) { + parsed_type_size = DATA_LLONG; } - 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); + parsed_type_size &= 0x1f; - } - -} - -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) { + if (is_current_typedef_name ()) { - for (;;) { - - char **init_symbols = 0; - int64_s *init_values = 0; + struct typedef_entry *entry; + + entry = find_typedef_name (tok.ident); + get_token (); + + load_typedef_name (entry); - char *name = 0; - int init_value_count = 0, i; + if (entry && entry->is_aggregate && entry->tag_name && entry->tag_name[0]) { - int object_fields[MAX_AGG_FIELDS]; - int object_field_count = 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; - const char *name_start, *name_caret; - unsigned long name_line; + } - int saved_field_count = parsed_field_count; + 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 declaration_is_inline; - int declaration_storage; - int declaration_dllimport = 0; + int aggregate_size = 0; + int aggregate_align = 1; - 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; + int aggregate_fields[MAX_AGG_FIELDS]; + int aggregate_field_count = 0; - long decl_array_count; - long decl_first_array_count; - long decl_last_array_count; + int union_init_fields[MAX_AGG_FIELDS]; + int union_init_field_count = 0; + int union_init_size = 0; - for (i = 0; i < saved_field_count; i++) { - object_fields[i] = parsed_field_sizes[i]; - } + struct aggregate_tag_entry *tag_entry = 0; - object_field_count = saved_field_count; + int aggregate_storage_class = parsed_storage_class; + int aggregate_is_inline = parsed_type_is_inline; - declaration_is_inline = parsed_type_is_inline; - declaration_storage = parsed_storage_class; - declaration_dllimport = parsed_dllimport; + char *tag_name = 0; + int member_info_start = member_info_count; - parse_declarator (&name); + parsed_type_size = DATA_PTR; + parsed_type_is_void = 0; - declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport; - apply_typedef_array_to_declarator (); + clear_parsed_fields (); + get_token (); - 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_first_array_count = declarator_first_array_count; - decl_last_array_count = declarator_last_array_count; + if (token_is_ident ()) { - name_start = last_declarator_name_start; - name_caret = last_declarator_name_caret; + 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 (); - name_line = last_declarator_name_line; + } - if (declaration_storage == STORAGE_TYPEDEF) { + if (tok.kind == TOK_LBRACE) { - if (name) { + get_token (); + + while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) { - make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate); + 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; + } + + } + + } - 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, - (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention, - object_fields, object_field_count); + expect (TOK_SEMI, ";"); } - 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 (); - - } + expect (TOK_RBRACE, "}"); - if (name) { - free (name); + if (aggregate_size <= 0) { + aggregate_size = DATA_CHAR & 0x1f; } - if (!_accept (TOK_COMMA)) { - break; - } + aggregate_size = (int) align_up_long (aggregate_size, aggregate_align); - continue; - - } - - if (name && declarator_has_function && (tok.kind == TOK_LBRACE || is_type_start (tok.kind))) { - - if (parsed_dllexport || declarator_dllexport) { + { - vec_push (&vec_dllexports, xstrdup (name)); + const char *owner_name = 0; + char anon_owner_name[64]; - declarator_dllexport = 0; - parsed_dllexport = 0; - - } - - 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); + int mi; + + if (has_tag) { + owner_name = tag_name; } 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; + sprintf (anon_owner_name, "", anonymous_aggregate_owner_id++); + owner_name = anon_owner_name; + } - - } 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 { - - { + for (mi = member_info_start; mi < member_info_count; mi++) { - 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); + if (member_infos[mi].owner_size == 0) { + member_infos[mi].owner_size = aggregate_size; } - global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses; + if (owner_name && member_infos[mi].owner_tag_name == 0) { + member_infos[mi].owner_tag_name = xstrdup (owner_name); + } } - if (decl_has_array && decl_array_unsized) { + if (!has_tag && owner_name) { - 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; - } + strncpy (parsed_type_tag_name, owner_name, sizeof (parsed_type_tag_name) - 1); + parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0'; } } - - } - - 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_first_array_count = decl_first_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. - */ - add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line); - - if (find_global_symbol (name) >= 0) { - - 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); - - set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention); - set_global_symbol_dllimport (name, declaration_dllimport); - - copy_pending_params_to_global_symbol (name); - - } + parsed_type_size = aggregate_size; + parsed_type_is_aggregate = 1; + parsed_type_is_void = 0; + parsed_type_has_tag = has_tag; - } else if ((declaration_storage == STORAGE_EXTERN || declaration_dllimport) && init_value_count == 0) { + clear_parsed_fields (); + + if (is_union) { - add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line); - - if (find_global_symbol (name) >= 0) { + int i; - 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); - set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); - set_global_symbol_dllimport (name, declaration_dllimport); + 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 { - 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_dimensions (name, declarator_has_array ? declarator_array_dimensions : 0); - set_global_symbol_array_element_size (name, declarator_array_element_size_now (parsed_type_size)); - - 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); + int i; + for (i = 0; i < aggregate_field_count; i++) { + append_parsed_field (aggregate_fields[i]); } } - free (name); - - } + 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); + + } - for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) { + } else { - if (init_symbols[i]) { + tag_entry = find_aggregate_tag (tag_name, is_union); + + if (tag_entry) { + load_aggregate_tag_fields (tag_entry); + } else { - free (init_symbols[i]); - init_symbols[i] = 0; + parsed_type_size = DATA_PTR; + append_parsed_field (DATA_PTR); } } - free (init_symbols); - free (init_values); - - if (!_accept (TOK_COMMA)) { - break; + if (tag_name) { + free (tag_name); } + + parsed_storage_class = aggregate_storage_class; + parsed_type_is_inline = aggregate_is_inline; + + return; } - 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 (tok.kind == TOK_ENUM) { - if (state->syntax & ASM_SYNTAX_MASM) { - - fprintf (state->ofp, ".386\n"); - fprintf (state->ofp, ".model flat\n"); + parsed_type_size = DATA_INT & 0x1f; + parsed_type_is_unsigned = 0; - } else if (state->syntax & ASM_SYNTAX_NASM) { + get_token (); - fprintf (state->ofp, "cpu 386\n"); - fprintf (state->ofp, "bits 32\n"); + if (token_is_ident ()) { + get_token (); + } - } else if (state->syntax & ASM_SYNTAX_INTEL) { - fprintf (state->ofp, ".intel_syntax noprefix\n"); + if (tok.kind == TOK_LBRACE) { + parse_enum_body (); } + + append_parsed_field (DATA_INT & 0x1f); + return; } - current_section = SECTION_NONE; + /*if (saw_void) { + parsed_type_size = DATA_INT; + }*/ - clear_global_symbols (); - clear_inline_functions (); - clear_enum_constants (); - clear_typedef_names (); + parsed_type_is_floating = (saw_float || saw_double) ? 1 : 0; - get_token (); + 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); - while (tok.kind != TOK_EOF) { + if (!saw) { - if (is_type_start (tok.kind)) { - - parse_type_spec (); - parse_external_after_type (); - - continue; + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected type"); + get_token (); + + } + +} + +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; } - - if (parse_possible_knr_function ()) { - continue; + + } else if (token_is_ident ()) { + + if (out_name && !*out_name) { + *out_name = take_ident (); + } else { + get_token (); } - - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected external declaration"); - get_token (); - - } - if (state->syntax & ASM_SYNTAX_MASM) { - masm_flush_data_line (); } - emit_pending_extern_symbols (); - - if (vec_dllexports.length) { + for (;;) { - char *p, *tmp; - - const char *name; - long i; - - FILE *fp; - - if ((p = strrchr (state->ofile, '.'))) { - - tmp = xmalloc (p - state->ofile + 5); - sprintf (tmp, "%.*s.def", (int) (p - state->ofile), state->ofile); - - } else { + if (_accept (TOK_LBRACK)) { - tmp = xmalloc (strlen (state->ofile) + 5); - sprintf (tmp, "%s.def", state->ofile); + int was_array = declarator_has_array; + long count = 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 (!was_array) { + declarator_first_array_count = count; + } + + declarator_has_array = 1; + declarator_array_dimensions++; + + if (declarator_array_count <= 0) { + declarator_array_count = 1; + } + + declarator_array_count *= count; + declarator_last_array_count = count; + + expect (TOK_RBRACK, "]"); + continue; } - if ((fp = fopen (tmp, "w"))) { + if (_accept (TOK_LPAREN)) { - fprintf (fp, "EXPORTS\n"); + int old_style_param_start = pending_param_count; + declarator_has_function = 1; - for (i = 0; i < vec_dllexports.length; i++) { + if (tok.kind != TOK_RPAREN) { - if ((name = asm_global_symbol_name (vec_dllexports.data[i]))) { - fprintf (fp, " %s\n", name); + for (;;) { + + if (tok.kind == TOK_ELLIPSIS) { + + declarator_function_is_variadic = 1; + get_token (); + + } else if (is_type_start (tok.kind)) { + + enum token_kind saved_calling_convention = declarator_calling_convention; + enum token_kind saved_parsed_calling_convention = parsed_calling_convention; + + 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), parsed_type_is_unsigned); + + } + + } else { + saw_void_param_list = 1; + } + + if (param_name) { + free (param_name); + } + + parsed_calling_convention = saved_parsed_calling_convention; + 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_calling_convention = saved_calling_convention; + 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) { + + if (find_pending_param_from (param_name, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", param_name); + } else { + add_pending_param (param_name, param_size, type_alignment (param_size), 0, 0, unknown_typedef_pointer ? 1 : 0, DATA_INT & 0x1f, 0); + } + + } + + free (param_name); + + declarator_function_param_count++; + declarator_function_has_prototype = 1; + consumed_as_prototype_param = 1; + + } + + if (!consumed_as_prototype_param) { + + if (declarator_depth == 1) { + + if (find_pending_param_from (maybe_type_name, old_style_param_start) >= 0) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", maybe_type_name); + } else { + add_pending_param (maybe_type_name, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0, 0); + } + + } + + 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; + } + } } - fclose (fp); - + expect (TOK_RPAREN, ")"); + continue; } - - } - - if (state->ofp) { - - if (state->syntax & ASM_SYNTAX_MASM) { - - masm_flush_data_line (); - fprintf (state->ofp, "end\n"); - } + break; } diff --git a/parse.h b/parse.h index 85318a2..0958ff0 100644 --- a/parse.h +++ b/parse.h @@ -4,15 +4,355 @@ #ifndef _PARSE_H #define _PARSE_H -void compile_translation_unit (void); +#include "int64.h" +#include "token.h" +#include "vector.h" + +#define DATA_NONE 0 +#define DATA_VOID 4 + +#define DATA_PTR (state->bits == 64 ? 8 : 4) +extern int anonymous_aggregate_owner_id; + +extern enum token_kind last_found_member_calling_convention; +extern enum token_kind postfix_member_calling_convention; +extern enum token_kind parsed_calling_convention; + +extern struct vector vec_dllexports; +extern int parsed_dllexport; +extern int parsed_dllimport; + +extern int parsed_type_size; +extern int parsed_type_is_inline; +extern int parsed_type_is_void; +extern int parsed_type_is_unsigned; +extern int parsed_type_is_floating; +extern int parsed_type_only_qualifiers; + +extern int parsed_type_is_aggregate; +extern int parsed_type_has_tag; + +extern char parsed_type_tag_name[128]; +extern char last_cast_type_tag_name[128]; + +extern int last_cast_type_object_size; +extern int last_cast_type_is_floating; +extern int floating_rhs_result_in_eax_bool; + +extern int parsed_type_is_array_typedef; +extern int parsed_type_array_element_size; + +extern long parsed_type_array_count; + +extern int declarator_depth; +extern int declarator_has_function; +extern int declarator_function_is_pointer; +extern int declarator_function_param_count; +extern int declarator_function_has_prototype; +extern int declarator_function_is_variadic; +extern int declarator_array_unsized; + +extern int declarator_is_pointer; +extern int declarator_pointer_depth; +extern int declarator_has_array; + +extern int declarator_dllexport; +extern int declarator_dllimport; + +extern enum token_kind declarator_calling_convention; +extern int global_initializer_accept_symbol_addresses; + +extern long declarator_array_count; +extern long declarator_first_array_count; +extern long declarator_last_array_count; + +extern int declarator_array_dimensions; + +#define STORAGE_NONE 0 +#define STORAGE_EXTERN 1 +#define STORAGE_STATIC 2 +#define STORAGE_TYPEDEF 3 + +extern int parsed_storage_class; + +#define MAX_GLOBAL_INIT_FIELDS 262144 +#define MAX_AGG_FIELDS 512 + +extern int parsed_field_sizes[MAX_AGG_FIELDS]; +extern int parsed_field_count; + +#define MAX_STRING_INIT_BYTES 4096 +int _accept (enum token_kind k); + +void compile_translation_unit32 (void); +void compile_translation_unit64 (void); int token_is_sizeof_keyword (void); int parse_sizeof_value (void); +int is_type_start (enum token_kind k); +int token_is_ms_int_type_name (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 parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer); + int resolve_enum_constant (const char *name, int64_s *out); +#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)) + +struct enum_const_entry { + + char *name; + int64_s value; + +}; + +#define MAX_ENUM_CONSTANTS 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; + + enum token_kind calling_convention; + int owner_size; + +}; + +#define MAX_MEMBER_INFOS 4096 + +extern struct member_info_entry member_infos[MAX_MEMBER_INFOS]; +extern int member_info_count; + +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 array_dimensions; + + int pointer_depth; + int pointed_size; + int pointed_is_floating; + int pointed_is_unsigned; + + char *pointed_tag_name; + char *tag_name; + +}; + +extern const char *last_found_member_tag_name; +extern int last_found_member_is_unsigned; + +struct aggregate_tag_entry { + + char *name; + + int is_union; + int size; + + int field_count; + int field_sizes[MAX_AGG_FIELDS]; + +}; + +#define MAX_LOCAL_SYMBOLS 1024 + +extern struct local_symbol local_symbols[MAX_LOCAL_SYMBOLS]; +extern int local_symbol_count; + +struct local_symbol *find_local_symbol (const char *name); + +#define MAX_GLOBAL_SYMBOLS 4096 +int find_global_symbol (const char *name); + +#define GLOBAL_SYMBOL_OBJECT 1 +#define GLOBAL_SYMBOL_FUNCTION 2 + +struct global_symbol_entry { + + char *name; + + int is_unsigned, is_extern; + int is_dllimport; + int kind, size; + + int array_element_size; + int is_array; + int array_dimensions; + + long array_count; + + int is_floating; + int pointer_depth; + int pointed_size; + int pointed_is_floating; + int pointed_is_unsigned; + int returns_void; + + int param_count; + int has_prototype; + int is_variadic; + + int param_sizes[128]; + int param_unsigneds[128]; + int param_floatings[128]; + int param_pointer_depths[128]; + + int import_call_stack_bytes; + int is_implicit; + int extern_emitted; + + enum token_kind calling_convention; + char *tag_name; + +}; + +extern struct global_symbol_entry global_symbols[MAX_GLOBAL_SYMBOLS]; +extern int global_symbol_count; + +int get_global_symbol_param_pointer_depth (const char *name, int param_index); +int get_global_symbol_array_element_size (const char *name); +int get_global_symbol_pointer_depth (const char *name); +int get_global_symbol_pointed_size (const char *name); +int get_global_symbol_array (const char *name); +int get_global_symbol_size (const char *name); + +int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating); + +#define MAX_AGG_TAGS 256 + +extern struct aggregate_tag_entry aggregate_tags[MAX_AGG_TAGS]; +extern int aggregate_tag_count; + +struct aggregate_tag_entry *find_aggregate_tag (const char *name, int is_union); +void parse_type_spec (void); + +#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; + + enum token_kind calling_convention; + +}; + +extern struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES]; +extern int typedef_name_count; + +extern int capture_declarator_name_location; +extern int captured_declarator_name_location; + +extern const char *captured_declarator_name_start; +extern const char *captured_declarator_name_caret; + +extern unsigned long captured_declarator_name_line; + +#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; + int pointed_is_floating; + int pointed_is_unsigned; + + char *pointed_tag_name; + int knr_declared; + +}; + +extern struct pending_param pending_params[MAX_PENDING_PARAMS]; + +extern int pending_param_count; +extern int preserve_pending_params; + +extern const char *last_declarator_name_start; +extern const char *last_declarator_name_caret; + +extern unsigned long last_declarator_name_line; +int declarator_object_size (int base_size); + +void parse_declarator_inner (char **out_name); +void parse_declarator (char **out_name); + +void parse_direct_declarator (char **out_name); +void append_parsed_field (int size); +void clear_parsed_fields (void); + +int expect (enum token_kind k, const char *what); +int is_string_token (void); + +extern struct enum_const_entry enum_constants[MAX_ENUM_CONSTANTS]; +extern int enum_constant_count; + +struct typedef_entry *find_typedef_name (const char *name); +int token_is_ident (void); + +extern int parsed_string_initializer_elem_size; +void parse_string_initializer_values (int64_s *values, int max_values, int *count); + +void append_global_init_value (int64_s *values, int max_values, int *count, unsigned int value, int elem_size); +void clear_pending_params (void); + +void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3); +int parse_constexpr_null_member_address_after_lparen (int64_s *out); + +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 type_alignment (int size); + +char *take_ident (void); +int find_pending_param_from (const char *name, int start); + +void add_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size, int pointed_is_unsigned); +long align_up_long (long value, int align); + #endif /* _PARSE_H */