--- /dev/null
+/******************************************************************************
+ * @file amd64.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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, <rhs-constant>
+ * 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, <enum>; 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");
+
+ }
+
+ }
+
+}
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);
}
int pedantic;
int mode;
- int std;
+
+ int bits, std;
int long64;
int syntax;
--- /dev/null
+/******************************************************************************
+ * @file i386.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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, <rhs-constant>
+ * 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, <enum>; 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");
+
+ }
+
+ }
+
+}
#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 },
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=<standard> Assume that the input sources are for\n");
fprintf (stderr, " <standard>.\n");
}
+ state->bits = 32;
+
while (optind < argc) {
r = argv[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;
}
- case CC_OPTION_DEFINE : {
+ case CC_OPTION_DEFINE: {
char *arg;
/******************************************************************************
* @file parse.c
*****************************************************************************/
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}
-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;
}
}
}
-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, "<anon-aggregate-%d>", 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, <rhs-constant>
- * 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, <enum>; 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 ()) {
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, "<anon-aggregate-%d>", 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;
}
#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 */